В Perl, как правильно для подкласса псевдоним метода в базовом классе? - PullRequest
12 голосов
/ 15 февраля 2010

Я просто ненавижу, как аксессор 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 отличается от того, что вы используете разум.

Ответы [ 4 ]

10 голосов
/ 15 февраля 2010

Ваш подход с 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:

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

Майкл Карман предложил объединить обе техники в самоопределяющуюся функцию:

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'";
}
3 голосов
/ 15 февраля 2010

Вы можете создать псевдоним подпрограмм, манипулируя таблицей символов:

*My::Merhaba::merhaba = \&My::Hello::hello;

Некоторые примеры можно найти здесь .

2 голосов
/ 15 февраля 2010

Я не уверен, что правильный путь, но Адам Кеннеди использует ваш второй метод (т.е. без goto) в Method :: Alias ​​ () нажмите здесь, чтобы перейти непосредственно к исходный код ).

1 голос
/ 16 февраля 2010

Это своего рода комбинация Quick-n-Dirty с незначительным косвенным использованием UNIVERSAL::can.

package My::Merhaba;
use base 'My::Hello';
# ...
*merhaba = __PACKAGE__->can( 'hello' );

И в этом пакете у вас будет подпрограмма "merhaba", которая будет иметь псевдоним My::Hello::hello. Вы просто говорите, что что бы этот пакет ни делал под именем hello, он может делать под именем merhaba.

Однако этого недостаточно, чтобы какой-то декоратор кода мог изменить подпрограмму, на которую указывает *My::Hello::hello{CODE}. В этом случае Method::Alias может быть подходящим способом для определения метода, как предполагают молекулы.

Однако, если это довольно хорошо управляемая библиотека, в которой вы управляете родительской и дочерней категориями, то приведенный выше метод slimmmer

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...