2012-10-19 14 views
6

Mam klasy bazowej, o nazwie Foo :: Base, muszę dziedziczą swoje metody, jak „nowy” i importować niektóre nazwy podprogramów w zakresie:Perl: Jak importować podprogramy z klasy bazowej?

package Foo::Base; 

sub new { ... } 

sub import { 
    no strict 'refs'; 

    my $caller = caller; 

    *{"${caller}::my_sub"} = sub { 1 }; 
} 

1; 

Więc muszę używać tej bazy klasy w mojej drugiej klasie Foo :: dziecka:

use base 'Foo::Base'; 

... i to działa dla dziedziczenia, ale to nie importuje „my_sub” w zakresie. Mogę dodać ciąg

use Foo::Base; 

dla niego i to pomaga, ale nie chcę, aby napisać coś takiego:

use base 'Foo::Base'; 
use Foo::Base; 

To wygląda trochę dziwne ... Czy są jakieś propozycje ten problem?

Odpowiedz

6

Podczas pisania use base używasz urządzeń z modułu base. I przekazujesz mu parametr modułu, który chcesz być klasą podstawową.

W relacji OO IS-A nie jest potrzebny żaden import. Wywołujesz metody o wzorze OO: $object_or_class->method_name(@args). Czasami oznacza to, że nie obchodzi kto invocant jest tak:

sub inherited_util { 
    my (undef, @args) = @_; 
    ... 
} 

lub

sub inherited2 { 
    shift; 
    ... 
} 

Jednak jeśli chcesz użytku narzędzi zdefiniowanych w module bazowym i dziedziczą z zachowania klasy zdefiniowanego w tym module, to dokładnie to, co wskazują dwie instrukcje użycia.

Nadal, jeśli masz dwa różne typy zachowań, które chcesz zastosować w modułach, prawdopodobnie lepiej jest podzielić rzeczy typu narzędzia na ich własny moduł i użyć go z obu modułów. W każdym razie zachowanie jawne jest często lepsze niż niejawne.

Jednak, użyłem tego wzoru przed:

sub import { 
    shift; 
    my ($inherit_flag) = @_; 
    my $inherit 
     = lc($inherit_flag) ne 'inherit' ? 0 
     : shift() && !!shift()    ? 1 
     :         0 
     ; 
    if ($inherit) { 
     no strict 'refs'; 
     push @{caller().'::ISA'}, __PACKAGE__; 
     ... 
    } 
    ... 
} 

tamtędy, ja wykonać jedno połączenie z wyraźną powiązanie zwyczajów.

use UtilityParent inherit => 1, qw<normal args>; 
+0

Tak, jest to bardzo podobne do Mojo :: Base lub Class :: Simple, thanks! Ale zdecydowałem się zrezygnować z lepszej architektury kodu =) – shootnix

14

Są dwa powody, dla których ktoś może chcieć robić to, co robisz, oba są złe.

Po pierwsze, próbujesz zaimportować metody z klasy nadrzędnej ... z jakiegoś powodu. Może źle rozumiesz, jak działa OO. Nie musisz tego robić. Po prostu wywołaj metody odziedziczone jako metody i jeśli te metody nie robią czegoś zwariowanego, to będzie działało dobrze.

Prawdopodobnie jest to moduł o mieszanym przeznaczeniu, w którym niektóre z nich są metodami, a niektóre z nich są importowanymi funkcjami. I za to możesz zrobić ...

use base 'Foo::Base'; 
use Foo::Base; 

I słusznie zauważyłeś, że wygląda trochę dziwnie ... ponieważ jest to trochę dziwne.Klasa, która również eksportuje, miesza idiomy, co spowoduje dziwne wzorce użycia.

Najlepszą rzeczą do zrobienia jest przeprojektowanie klasy zamiast eksportowania funkcji, albo podzielenie funkcji na ich własny moduł, albo uczynienie ich metodami klasy. Jeśli funkcje naprawdę nie mają wiele wspólnego z klasą, to najlepiej je spryskać. Jeśli odnoszą się do klasy, wtedy uczyń je metodami klasowymi.

use base 'Foo::Base'; 

Foo::Base->some_function_that_used_to_be_exported; 

Eliminuje to niedopasowanie interfejsu, a jako bonus, podklasy można zastąpić zachowanie metoda klasy, podobnie jak każdej innej metody.

package Bar; 

use base 'Foo::Base'; 

# override 
sub some_function_that_used_to_be_exported { 
    my($class, @args) = @_; 

    ...do something extra maybe... 

    $class->SUPER::some_function_that_used_to_be_exported(@args); 

    ...and maybe something else... 
} 

Jeśli nie masz kontroli nad klasie bazowej, można jeszcze zrobić interfejs Sane pisząc podklasę, która zamienia wyeksportowane funkcje do metod.

package SaneFoo; 

use base 'Foo::Base'; 

# For each function exported by Foo::Base, create a wrapper class 
# method which throws away the first argument (the class name) and 
# calls the function. 
for my $name (@Foo::Base::EXPORT, @Foo::Base::EXPORT_OK) { 
    my $function = Foo::Base->can($name); 
    *{$name} = sub { 
     my $class = shift; 
     return $function->(@_); 
    }; 
} 
+0

Właściwie chciałem dodać do tych klas trochę cukru syntaktycznego, dokładnie tak jak w Mojo :: Base, ale masz rację, dziękuję - za implementację, która jest przesadą dla mojego obecnego Zadanie, i gdybym mógł użyć Mojo :: Base, zrobiłbym to, ale nie mogę. – shootnix

+0

W takim przypadku użyj konwencji 'używaj bazy Foo ', takiej jak Mojo :: Base. Nie polecałbym iść na szlak Moose, gdzie 'use Foo' zarówno eksportuje, jak i dziedziczy. To łamie zbyt wiele konwencji dla J :: Random :: Module, aby to zrobić. Chyba że będzie to Twój własny odpowiednik w Moose'u. Soooo ... dlaczego "nie mogę" używasz Mojo :: Base? – Schwern

1

Aby wyjaśnić nieco więcej na pytanie „ale dlaczego nie import w Foo :: Dziecko pracy?” Oto program, który pisze, co się rzeczywiście dzieje:

use Foo::Child; 
print my_sub(); # Really? Yes, really! 
print Foo::Child::my_sub()," done\n"; 

i dodaje to do Foo :: Base :: import() rutynowe:

print "Caller is $caller\n"; 

po uruchomieniu tego, jak widać to na wyjściu:

Caller is main 
1 
Undefined subroutine &Foo::Child::my_sub called at foo_user.pl line 4. 

Widzimy raport Foo :: Base, który informuje nas, kto jest dzwoniącym: to program główny! Widzimy "1", co udowadnia, że ​​tak, główne :: my_sub już istnieje, a następnie błąd, ponieważ import trafił do niewłaściwej przestrzeni nazw.

Dlaczego tak jest? Ponieważ proces importowania jest obsługiwany przez program główny. Import Foo :: Base nie jest wywoływany przez nic w Foo :: Child; zostaje wywołany przez program główny podczas ładowania modułów. Jeśli naprawdę, naprawdę chcesz wymusić subskrybowanie, w przeciwieństwie do metod, do zaimportowania do Foo :: Child, musisz jawnie zrobić to w samym Foo :: Child. "Użyj Foo :: Base" zrobi to, ponieważ spowoduje to, że import zostanie wykonany z Foo :: Child jako dzwoniącym; Jeśli nalegasz na importowanie, ale podwójne kliknięcie zbytnio się nie podoba, możesz zadzwonić do Foo :: Base :: import() zaraz po 'use base'. Dokładnie to robi podwójne "używanie".

Niemniej jednak lubię metody klasy Schwern lepiej, i polecam tę alternatywę.

Powiązane problemy