2010-02-15 9 views
12

Po prostu nienawidzę tego, jak akcesor CGI::Application dla obiektu nosi nazwę .W języku Perl, jaki jest właściwy sposób dla podklasy do aliasów metody w klasie bazowej?

Chciałbym moje klasy instancji, aby móc korzystać z akcesor nazwie cgi aby uzyskać obiekt CGI związanego z bieżącej instancji mojego CGI::Application podklasy.

Oto samowystarczalny przykładem tego, co robię:

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'); 

To działa jak myślę, że powinien i nie widzę żadnych problemów (powiedzmy, jeśli chcę dziedziczyć My::Merhaba: podklasy nie musisz nic wiedzieć o merhaba).

Czy byłoby lepiej/bardziej poprawne pisać

sub merhaba { 
    my $self = shift; 
    return $self->hello(@_); 
} 

Jakie są zalety/wady korzystania goto &NAME dla celów aliasing nazwę metody? Czy istnieje lepszy sposób?

Uwaga: Jeśli masz ochotę odpowiedzieć goto jest zła nie rób tego, bo to wykorzystanie Perl goto jest inna niż to, co masz na myśli.

+0

Nie widzę sensu używania 'goto & NAME' w ten sposób. Opakowanie 'sub {}' dodaje nową ramkę do stosu wywołań. Równie dobrze możesz wywołać metodę bezpośrednio. –

+0

@Michael Carman używający 'can', jak w pierwszej części odpowiedzi @Eric Strom, eliminuje ten problem. –

+0

Po prostu byłem ciekawy postępów w metodzie "Metoda :: Alias". –

Odpowiedz

10

Twoje podejście z goto jest właściwe, ponieważ zapewnia, że ​​caller/wantarray i tym podobne działają poprawnie.

bym setup nowa metoda tak:

sub merhaba { 
    if (my $method = eval {$_[0]->can('hello')}) { 
     goto &$method 
    } else { 
     # error code here 
    } 
} 

Albo jeśli nie chcesz korzystać z dziedziczenia, można dodać nową metodę do istniejącego pakietu z kodu wywołującego:

*My::Hello::merhaba = \&My::Hello::hello; 
    # or you can use = My::Hello->can('hello'); 

to można nazwać:

My::Hello->merhaba('StackOverflow'); 

i uzyskać pożądany rezultat.

W obu przypadkach ścieżka dziedziczenia jest łatwiejsza w utrzymaniu, ale dodanie metody do istniejącego pakietu spowoduje szybsze wywołania metod.

Edit:

Jak zauważył w komentarzach, istnieje kilka przypadków przypisanie glob będzie trwał konflikt z dziedziczenia, więc w razie wątpliwości, należy użyć pierwszej metody (tworzenie nowej metody w pakiecie sub).

Michael Carman zaproponował połączenie obu technik w zależności siebie redefinicji:

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'"; 
} 
+0

czy byłyby przypadki dziedziczenia, że ​​'* My :: Hello :: merhaba = My :: Hello-> can ('hello')' nie będzie działać? Każda podklasa będzie widzieć merhaba jako właściwą metodę, a nawet jeśli 'hello' będzie obsługiwane przez dziedziczenie w pakiecie macierzystym, to i tak znajdzie właściwą metodę. Nie zadziałałoby, gdyby "cześć" zostało dostarczone z autoloaderem, a autoloader zachował pewien stan (nie był czysty w sensie funkcjonalnym). –

+0

+1 Dobra robota. Przepraszam, że wysłałem konkurencyjną odpowiedź. Po prostu nie rozumiałem na początku, w jaki sposób 'caller' /' wantarray' jest związany z tym pytaniem. –

+0

Storm '* My :: Hello :: merhaba = Mój :: Hello-> can ('hello')' nie zrobi tego, co należy, jeśli któraś z podklas nadpisze 'hello'. Nie wiem, czy istnieje sposób na to, by zadanie związane z globem działało prawidłowo z dziedziczeniem. Wolę zaakceptować odpowiedź, która nie wkracza w problematyczne terytorium. –

3

Można alias podprogramów manipulując tablicę symboli:

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

Niektóre przykłady można znaleźć here.

+1

To zawsze wywoływałoby 'My :: Hello :: hello' bez względu na dziedziczenie. –

2

nie jestem pewien, co jest we właściwy sposób, ale Adam Kennedy wykorzystuje swoją drugą metodę (bez goto) w Method::Alias (click here to go directly to the source code).

+0

Dzięki. Pomogło mi to zrozumieć odpowiedź Erica Stroma. Tuż przed cytatem, o którym wspomniałeś, Adam mówi: * "Zauważ, że dodaje to dodatkowy wpis do tablicy wywołującej, ale nie jest to aż tak ważne, chyba że jesteś paranoikiem na temat tych rzeczy." * Oznacza to, że używając ' goto' jest lepsze, ponieważ unika dodawania * "dodatkowego wpisu do tablicy wywołującej" *. Wpłynęłoby to na 'brak przejrzystości '(jak wspomniał Eric Strom), ponieważ zmienia postrzeganie tego, co prawdziwa metoda" chce ". Czy to prawda? –

+1

Nie sądzę, że wpłynie to na 'wantarray'; kontekst powinien propagować poprawnie w obu przypadkach, o ile wywołanie jest ostatnią instrukcją w opakowaniu. to znaczy, że nie działa to w sposób podobny do 'my @results = $ self-> method(); return @ results'. Użycie 'goto' spowoduje ukrycie ramki wywołania, co jest pomocne, jeśli metoda używa' caller' (mało prawdopodobne) lub rzeczy takie jak 'carp' lub' croak' (bardziej prawdopodobne). –

+1

@molecules => zakładając, że metoda zostanie wywołana w odpowiednim kontekście powrotu (nieprzypisana do zmiennej, a następnie zwrócona lub coś podobnego (przykład Sinana jest w porządku)), wtedy kontekst będzie propagował dalej, niezależnie od dodatkowych ramek stosu, I wspomniałem o tym w mojej odpowiedzi, aby podkreślić, że 'goto' zajmuje się zarówno –

1

Jest to rodzaj kombinacji Quick-n-Dirty z odrobiną indirection przy użyciu UNIVERSAL::can.

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

i będziesz mieć sub nazywa „merhaba” w tym pakiecie, że aliasy My::Hello::hello. Mówisz po prostu, że niezależnie od tego, co zrobiłby ten pakiet pod nazwą hello, może to zrobić pod nazwą merhaba.

Jest to jednak niewystarczające, ponieważ jakiś dekorator kodu może zmienić podpunkt, który wskazuje na *My::Hello::hello{CODE}. W takim przypadku, Method::Alias może być właściwym sposobem określenia metody, jak sugeruje molekuła.

Jeśli jednak jest to raczej dobrze kontrolowana biblioteka, w której kontrolujesz zarówno kategorie nadrzędne, jak i podrzędne, powyższa metoda to slimmmer.

Powiązane problemy