Подтвердить что ты не робот

В 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 отличается от того, что вы имеете в виду.

4b9b3361

Ответ 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;

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

Ответ 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.