9

先週、サブクラスのメソッドを誤ってオーバーライドしてしまい、2 度噛みつきました。私は継承のファンではありませんが、職場のアプリケーションでこれを (ab) 使用しています。私がやりたいのは、メソッドが親メソッドをオーバーライドしていることを示すための宣言構文を提供することです。このようなもの:

use Attribute::Override;

use parent 'Some::Class';

sub foo : override { ... } # fails if it doesn't override
sub bar { ... }            # fails if it does override

ここにはいくつかの問題があります。まず、メソッドのロードが何らかの理由で遅延した場合 (たとえば、AUTOLOAD を介してロードされたメソッドや、後でシンボル テーブルにインストールされたメソッド)、これはそれらのメソッドを検出しません。

継承ツリーのウォークも同様にコストがかかる可能性があります。Class::Sniffでこれを行いますが、コードの実行にはあまり適していません。継承ツリーをたどって、適切なシンボル テーブルに定義された CODE スロットがある場所を単純に一致させることができます。その方が高速ですが、メソッド キャッシュが無効になっている場合、それらの結果をキャッシュすると壊れてしまいます。

だから私は2つの質問があります.これは合理的なアプローチですか?メソッドキャッシュが変更されたかどうかを確認できるフックはありますか? (「perldoc perlobj」で「キャッシュ」を検索してください)。

もちろん、これは本番コードを壊すべきではありません.TEST_HARNESS環境変数がアクティブな場合にのみ失敗するか警告することを考えています(本番コードがTEST_HARNESSを設定する場合は、強制的に非アクティブにするための明示的な環境変数を持っています)何らかの理由で環境変数)。

4

1 に答える 1

6

これを強制する 1 つの方法:

package Base;
...
sub new {
    my $class = shift;
    ...
    check_overrides( $class );
    ...
}

sub check_overrides {
    my $class = shift;
    for my $method ( @unoverridable ) {
        die "horribly" if __PACKAGE__->can( $method ) != $class->can( $method );
    }
}

check_overrides のメモ化が役立つ場合があります。

例外が必要な場合がある場合は、別のメソッド名を用意し、基本クラスで次のように呼び出します。

package Base;
...
my @unoverridable = 'DESTROY';
sub destroy {}
sub DESTROY {
    my $self = shift;
    # do essential stuff
    ...
    $self->destroy();
}
于 2009-03-08T18:38:46.053 に答える