2015-02-06 18 views
5

Mam ten obiekt perla. Po utworzeniu obiektu próbuję dodać nową metodę do obiektu w metodzie programu ładującego, którą można później wywołać.Dodaj nową metodę do istniejącego obiektu w perlu

Próbowałem całej masy rzeczy, które nie działały. Przykłady obejmują:

sub loader { 
    my ($self) = @_; 

    sub add_me { 
     my ($self, $rec) = @_ 

     warn "yayyyyyy"; 
     return $rec; 
    } 

    #here are the things I've tried that dont work: 
    # &{$self->{add_me}} = \&add_me; 
    # \&{$self->{add_me}} = \&add_me; 
    # assuming the class definition is in Holder::Class try to add it to symblol table 
    # *{Holder::Class::add_me} = \&add_me; 

} 

EDIT:

Dlatego, że muszę zrobić to Dodaję hak w moim kodu, gdzie użytkownik mojego oprogramowania będą mogli wstrzyknąć własnych podrzędny, aby edytować strukturę danych, tak jak będą.

Aby to zrobić, będą mogli edytować plik wtórnego, który będzie zawierał tylko jeden sub i uzyskać strukturę danych w pytaniu przeszedł w, więc coś takiego:

sub inject_a_sub { 
    my ($self, $rec) = @_; 

    #do stuff to $rec 

    return $rec; 
} 

następnie wewnątrz mojego oryginalnego obiektu na jego wystąpienie, sprawdzam, czy istnieje wyżej wymieniony plik, a jeśli tak, przeczytaj jego zawartość i przeanalizuj je. Na koniec chcę utworzyć kod eval'd, który jest tylko subem, metodą mojego obiektu. Mówiąc dokładniej, mój obiekt dziedziczy już metodę o nazwie do_something i chcę, aby sub-read został wprowadzony przez eval nadpisać dziedziczoną metodę do_something, tak aby po wywołaniu sub z pliku zewnętrznego działała.

Jego dziwny błąd:/

i boli mnie :(

Obi-Wan Kenobi jesteś moją jedyną nadzieją

Cheers

+2

Chcesz dodać dodatkowe metody do konkretnych obiektów, a nie do wszystkich obiektów klasy? czy istnieje powód, dla którego nie można podklasy? – ysth

+0

Obiekty nie mają metod, klasy mają metody. – ikegami

+1

@ikegami: To zależy od modelu. Jedyne, na co naprawdę zależy Ci to, że instancja ma wszystkie metody określone przez jej klasę. Ruby ma zarówno metody klasy (które mogą być wywoływane tylko w klasie, nie w instancji), jak i metody instancji, które są zdefiniowane w klasie, ale są dostępne tylko za pośrednictwem instancji tej klasy. Posiada również * metody singleton *, które są unikalne dla danej instancji, co jest tym, czego ten OP wydaje się potrzebować. – Borodin

Odpowiedz

9

jeśli po prostu chcesz dołączyć! do określonego obiektu i nie potrzebujesz dziedziczenia, możesz zapisać referencję kodu w obiekcie i wywołać go.

# Store the code in the object, putting it in its own 
# nested hash to reduce the chance of collisions. 
$obj->{__actions}{something} = sub { ... }; 

# Run the code 
my @stuff = $obj->{__actions}{something}->(@args); 

Problem polega na tym, że należy sprawdzić, czy $obj->{__actions}{something} zawiera odwołanie do kodu. Sugerowałbym owinięcie metody wokół tej procedury.

sub add_action { 
    my($self, $action, $code) = @_; 

    $self->{__actions}{$action} = $code; 

    return; 
} 

sub take_action { 
    my($self, $action, $args) = @_; 

    my $code = $self->{__actions}{$action}; 
    return if !$code or ref $code ne 'CODE'; 

    return $code->(@$args); 
} 

$obj->add_action("something", sub { ... }); 
$obj->take_action("something", \@args); 

Jeśli już znasz nazwę klasy, którą chcesz wprowadzić do metody, napisać podprogram jako normalne, ale używać pełnej nazwy.

sub Some::Class::new_method { 
    my $self = shift; 

    ... 
} 

pamiętać, że wszelkie globalne wewnątrz tego podprogramu będzie w otaczającym pakietu nie w Some :: Klasy. Jeśli chcesz używać zmiennych trwałych, użyj podprogramu state wewnątrz podprogramu lub my poza podprogramem.


Jeśli nie znasz nazwy w czasie kompilacji, będziesz musiał wstrzyknąć podprogram do tablicy symboli, więc byłaś blisko z tym ostatnim.

sub inject_method { 
    my($object, $method_name, $code_ref) = @_; 

    # Get the class of the object 
    my $class = ref $object; 

    { 
     # We need to use symbolic references. 
     no strict 'refs'; 

     # Shove the code reference into the class' symbol table. 
     *{$class.'::'.$method_name} = $code_ref; 
    } 

    return; 
} 

inject_method($obj, "new_method", sub { ... }); 

Metody w języku Perl są powiązane z klasą, a nie z obiektem. Aby przypisać metodę do pojedynczego obiektu, musisz umieścić ten obiekt we własnej klasie.Podobnie do powyższego, ale musisz utworzyć podklasę dla każdej instancji.

my $instance_class = "_SPECIAL_INSTANCE_CLASS_"; 
my $instance_class_increment = "AAAAAAAAAAAAAAAAAA"; 
sub inject_method_into_instance { 
    my($object, $method_name, $code_ref) = @_; 

    # Get the class of the object 
    my $old_class = ref $object; 

    # Get the special instance class and increment it. 
    # Yes, incrementing works on strings. 
    my $new_class = $instance_class . '::' . $instance_class_increment++; 

    { 
     # We need to use symbolic references. 
     no strict 'refs'; 

     # Create its own subclass 
     @{$new_class.'::ISA'} = ($old_class); 

     # Shove the code reference into the class' symbol table. 
     *{$new_class.'::'.$method_name} = $code_ref; 

     # Rebless the object to its own subclass 
     bless $object, $new_class; 
    } 

    return; 
} 

Pominąłem kod, aby sprawdzić, czy instancja ma już tego leczenia przez sprawdzenie, czy jego klasa pasuje /^${instance_class}::/. Zostawiam to jako ćwiczenie dla ciebie. Tworzenie nowej klasy dla każdego obiektu nie jest tanie i kosztuje pamięć.


Istnieją ku temu ważne powody, ale są wyjątkowe. Naprawdę, naprawdę powinieneś zastanowić się, czy powinieneś robić tego rodzaju monkey patching. Ogólnie należy unikać stosowania action at a distance.

Czy możesz zrobić to samo, używając podklasy, delegacji lub roli?

Istnieje już system Perl OO, który zrobi to za Ciebie i znacznie więcej. Powinieneś używać jednego. Moose, Moo (poprzez Role::Tiny) i Mouse mogą wszystkie dodać role do instancji.

+0

W pewnym sensie muszę przyjąć to z wiarą, że jest to jeden z tych wyjątkowych przypadków, ponieważ 15-letni perl powiedział mi, abym robił to w ten sposób, zamiast działającego sposobu, w jaki pierwotnie rozwiązałem problem ... :(jednak teraz, gdy Uruchamiam '* {$ class. ':: add_me'} = \ & add_me;' Pojawia się błąd: 'Nie można użyć ciągu (" MYCLASSNAME "...) jako symbolu ref, podczas gdy" ścisłe znaki "w użyciu' (gdzie MYCLASSNAME jest wartością klasy $) – Rooster

+0

Re "Aby przypisać metodę do pojedynczego obiektu, musisz umieścić ten obiekt we własnej klasie" lub użyć autoloadera. SUB AUTOLOAD {...; my $ method = $ self -> {metody} -> {$ method_name} lub die ...; $ self -> $ method (@_);} ' – ikegami

+0

@ikegami whoops .... ah, nie widział edycji (lub być może nie czytałem kompletnie) AMS spróbuj teraz – Rooster

Powiązane problemy