В Perl, каков правильный путь к подклассу для искажения метода в базовом классе?

Я просто ненавижу как CGI:: средство доступа Приложения для 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 отличается, чем, что Вы имеете в виду.

12
задан Sinan Ünür 15 February 2010 в 15:17
поделиться

4 ответа

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

и получить желаемый результат.

Любой способ будет работать, путь наследования более удобен, но добавление метода в существующий пакет приведет к более быстрому вызову метода.

Редактировать:

Как было отмечено в комментариях, есть несколько случаев, когда присваивание 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'";
}
10
ответ дан 2 December 2019 в 20:17
поделиться

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

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

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

3
ответ дан 2 December 2019 в 20:17
поделиться

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

2
ответ дан 2 December 2019 в 20:17
поделиться

Это своего рода комбинация 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 ​​ может быть подходящим способом указать метод, как предполагают молекулы.

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

1
ответ дан 2 December 2019 в 20:17
поделиться
Другие вопросы по тегам:

Похожие вопросы: