2015-02-06 4 views
5

이 perl 객체가 있습니다. 개체가 인스턴스화 된 후에는 나중에 호출 할 수있는 로더 메서드 내에서 개체에 새 메서드를 추가하려고합니다.perl의 기존 객체에 새 메소드 추가

나는 일하지 않은 모든 것들을 시도했다. 예는 다음과 같습니다

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; 

} 

편집 :

나는 이것이 내 소프트웨어의 사용자가 자신의를 주입 할 수있는 능력을 가지고 어디에 내 코드에 후크를 추가 해요입니다해야 할 이유 sub를 사용하여 데이터 구조를 편집하십시오.

이렇게하려면, 그들은 하나 개의 하위를 포함하고 전달 문제의 데이터 구조를 얻을 것이다 보조 파일을 편집 할 수 있습니다, 그래서 뭔가 같은 : 내 원래의 객체 내부의 다음

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

    #do stuff to $rec 

    return $rec; 
} 

시 그 인스턴스화, 나는 위에서 언급 한 파일이 있는지 확인하고 만약 그렇다면 그 내용을 읽고 평가한다. 마지막으로, 내 객체의 메소드 인 하위 코드 인 eval 된 코드를 만들고 싶습니다. 정확히 말하면, 내 객체는 이미 do_something이라는 메소드를 상속 받았으며, 외부 파일에서 서브가 호출 될 때 do_something 메소드가 상속 됨으로써 eval이 서브 클래스를 읽도록합니다.

그에게 이상한 문제 :!/

그것은 당신이 내 유일한 희망이야 케노비 나 :(

오비 완 상처

건배

+2

클래스의 모든 개체가 아닌 특정 개체에 추가 메서드를 추가 하시겠습니까? 하위 클래스를 만들 수없는 이유가 무엇입니까? – ysth

+0

개체에는 메서드가 없으며 클래스에는 메서드가 있습니다. – ikegami

+1

@ikegami : 귀하의 모델에 따라 다릅니다. 당신이 정말로 신경 쓰는 것은 인스턴스가 클래스에 의해 지정된 모든 메소드를 가지고 있다는 것입니다. Ruby에는 클래스에 정의되어 있지만 해당 클래스의 인스턴스를 통해서만 사용할 수있는 클래스 메소드 (인스턴스가 아닌 클래스에서만 호출 할 수 있음)와 인스턴스 메소드가 있습니다. 또한 주어진 인스턴스에 고유 한 * 싱글 톤 메소드 *가 있는데,이 OP가 필요로하는 것입니다. – Borodin

답변

9

그냥 첨부 할 경우! 기능을 특정 객체에 추가하고 상속 할 필요가없는 경우 객체에 코드 참조를 저장하고 호출 할 수 있습니다.

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

문제는 $obj->{__actions}{something}에 코드 참조가 있는지 확인해야합니다. 내가 제안하는 것은이 절차를 둘러싼 메소드를 랩핑하는 것이다.

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

당신은 이미 당신이로 방법을 주입 정상적으로 서브 루틴을 작성하지만, 완전한 이름을 사용할 클래스 이름을 알고있는 경우. 서브 루틴 내부에 전역이 하지 일부 :: 클래스에서, 주변의 패키지에 될 것

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

    ... 
} 

참고. 영구 변수를 원한다면 서브 루틴 내에 state을 사용하거나 서브 루틴 외부에 my을 사용하십시오. 당신이 컴파일시에 이름을 모르는 경우 당신이 마지막에 가까웠다 있도록


, 당신은, 심볼 테이블에 서브 루틴을 주입해야합니다.

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 { ... }); 

Perl의 메소드는 객체가 아닌 클래스와 연관되어 있습니다. 단일 객체에 메소드를 할당하려면 해당 객체를 자체 클래스에 넣어야합니다.위와 비슷하지만 모든 인스턴스에 대해 하위 클래스를 만들어야합니다.

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; 
} 

나는 인스턴스가 이미 클래스가 /^${instance_class}::/ 일치하는 경우 확인하여이 치료를했다 여부를 확인하는 코드를 떠났다. 나는 그것을 당신을위한 운동으로 남겨 둡니다. 모든 객체에 대해 새로운 클래스를 만드는 것은 비용이 적게 들고 비용이 많이 듭니다.


유효한 이유가 있지만 예외적입니다. 이 종류의 monkey patching을 수행해야하는지 여부에 대해 정말로 질문해야합니다. 일반적으로 action at a distance은 피해야합니다.

하위 클래스, 위임 또는 역할을 사용하여 동일한 작업을 수행 할 수 있습니까?

당신을 위해 훨씬 더 많은 것을 할 Perl OO 시스템이 이미 존재합니다. 당신은 하나를 사용해야합니다. Moose, Moo (Role::Tiny 통해) 및 Mouse은 모두 인스턴스에 역할을 추가 할 수 있습니다.

+0

15 년 동안 perl을 사용하는 사람이 원래 문제를 해결하는 작업 방식 대신이 방법을 사용하라고 말한 것처럼 예외적 인 경우 중 하나라고 생각합니다. (그러나 지금은 "* {$ class. ':: add_me'} = \ & add_me;'오류가 발생했습니다 :'strict refs in use'가 실행되는 동안 문자열 ('MYCLASSNAME"...)을 심벌 ref로 사용할 수 없습니다. (여기서 MYCLASSNAME은 $ class의 값입니다.) – Rooster

+0

"하나의 객체에 메소드를 할당하려면 그 객체를 자신의 클래스에 넣거나 오토로더를 사용해야합니다."sub AUTOLOAD {...; my $ self -> $ method (@_);}' – ikegami

+0

@ikegami whoops .... 아, 편집을 보지 못했습니다. ($ method = $ self -> {methods} 또는 아마도 완전히 읽지 못했을 것입니다.) – Rooster

관련 문제