В Perl, как правильно подобрать подкласс для метода в базовом классе?
Я просто ненавижу, как CGI::Application аксессор для объекта CGI
называется query
.
Я хотел бы, чтобы мои классы экземпляров могли использовать аксессор с именем CGI
, чтобы получить объект CGI
, связанный с текущим экземпляром моего подкласса CGI::Application
.
Вот один из примеров того, что я делаю:
package My::Hello;
sub hello {
my $self =shift;
print "Hello @_\n";
}
package My::Merhaba;
use base 'My::Hello';
sub merhaba {
goto sub { shift->hello(@_) };
}
package main;
My::Merhaba->merhaba('StackOverflow');
Это работает так, как мне кажется, и я не вижу никаких проблем (скажем, если бы я хотел наследовать от My::Merhaba
: подклассы не должны ничего знать о merhaba
).
Было бы лучше/правильнее писать
sub merhaba {
my $self = shift;
return $self->hello(@_);
}
Каковы преимущества/недостатки использования goto &NAME
с целью наложения имени метода? Есть ли лучший способ?
Примечание. Если у вас есть желание ответить с помощью goto
, зло не делает этого, потому что это использование Perl goto
отличается от того, что вы имеете в виду.
Ответы
Ответ 1
Ваш подход с goto
является правильным, потому что он гарантирует, что caller
/wantarray
и т.п. продолжат работать должным образом.
Я бы установил новый метод следующим образом:
sub merhaba {
if (my $method = eval {$_[0]->can('hello')}) {
goto &$method
} else {
# error code here
}
}
Или, если вы не хотите использовать наследование, вы можете добавить новый метод к существующему пакету из вашего кода вызова:
*My::Hello::merhaba = \&My::Hello::hello;
# or you can use = My::Hello->can('hello');
то вы можете позвонить:
My::Hello->merhaba('StackOverflow');
и получите желаемый результат.
В любом случае, путь наследования более удобен в обслуживании, но добавление метода к существующему пакету приведет к более быстрым вызовам методов.
Edit:
Как отмечалось в комментариях, существует несколько случаев, когда назначение glob будет работать с наследованием, поэтому, если есть сомнения, используйте первый метод (создание нового метода в подкомплексе).
Майкл Карман предложил объединить оба метода в самостоятельную переопределяющую функцию:
sub merhaba {
if (my $method = eval { $_[0]->can('hello') }) {
no warnings 'redefine';
*merhaba = $method;
goto &merhaba;
}
die "Can't make 'merhaba' an alias for 'hello'";
}
Ответ 2
Вы можете псевдонизировать подпрограммы, управляя таблицей символов:
*My::Merhaba::merhaba = \&My::Hello::hello;
Некоторые примеры можно найти здесь.
Ответ 3
Я не уверен, какой правильный путь, но Адам Кеннеди использует ваш второй метод (т.е. без goto
) в Method::Alias (нажмите здесь, чтобы перейти непосредственно к исходному коду).
Ответ 4
Это своего рода комбинация Quick-n-Dirty с модификацией косвенности с помощью UNIVERSAL::can
.
package My::Merhaba;
use base 'My::Hello';
# ...
*merhaba = __PACKAGE__->can( 'hello' );
И в этом пакете вы получите подкачку "merhaba", которая будет псевдонимом My::Hello::hello
. Вы просто говорите, что независимо от того, что этот пакет в противном случае сделал бы под именем hello
, он может делать под именем merhaba
.
Однако этого недостаточно для возможности того, что какой-либо декодер кода может изменить sub, на который указывает *My::Hello::hello{CODE}
. В этом случае Method::Alias
может быть подходящим способом указания метода, как предполагают молекулы.
Однако, если это довольно хорошо контролируемая библиотека, где вы управляете как родительскими, так и дочерними категориями, то вышеописанный метод является slimmmer.