Обнаружение переопределенных методов в Perl - PullRequest
9 голосов
/ 08 марта 2009

На прошлой неделе я был укушен дважды случайно переопределенными методами в подклассе. Хотя я не фанат наследования, мы (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 в соответствующей таблице символов, и это было бы быстрее, но если кэш метода будет признан недействительным, это сломалось бы, если бы я кешировал эти результаты.

Итак, у меня два вопроса: это разумный подход и есть ли ловушка, которая позволяет мне проверить, изменился ли кэш метода? (найдите «cache» в «perldoc perlobj»).

Конечно, это не должно нарушать производственный код, я думаю только о том, чтобы он завершился с ошибкой или предупредил, если переменная среды TEST_HARNESS активна (и имеет явную переменную среды, чтобы заставить ее быть неактивной, если производственный код должен был установить переменную окружения TEST_HARNESS по некоторым причинам).

1 Ответ

6 голосов
/ 08 марта 2009

Один из способов обеспечить это:

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();
}
...