perl

2015-02-06 10 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 inject_a_sub { 
    my ($self, $rec) = @_; 

    #do stuff to $rec 

    return $rec; 
} 
तो अपने मूल ऑब्जेक्ट के अंदर

पर:

ऐसा करने के लिए, वे कुछ की तरह एक माध्यमिक फ़ाइल है कि केवल एक उप होते हैं और में पारित प्रश्न में डेटा संरचना मिल जाएगा संपादित करने में सक्षम हो जाएगा, तो इसकी तात्कालिकता, मैं यह देखने के लिए जांच करता हूं कि उपर्युक्त फ़ाइल मौजूद है या नहीं, और यदि इसकी सामग्री पढ़ी जाती है और उन्हें eval। आखिरकार, मैं eval'd कोड बनाना चाहता हूं जो सिर्फ एक उप, मेरी वस्तु का एक तरीका है। सटीक होने के लिए, मेरी ऑब्जेक्ट पहले से ही do_something नामक एक विधि को विरासत में ले रही है और मैं do_something विरासत में प्राप्त होने वाली विधि को ओवरराइड करके उप-पठन करना चाहता हूं ताकि बाहरी फ़ाइल से उप को कॉल किया जा सके।

इसकी एक अजीब समस्या:/

और यह दर्द होता है मुझे :(

ओबी वॉन केनोबी तुम मेरे ही उम्मीद कर रहे हैं

चीयर्स

+2

आप विशिष्ट वस्तुओं के लिए अतिरिक्त विधियों को जोड़ना चाहते हैं, कक्षा की सभी वस्तुओं के लिए नहीं? क्या कोई कारण है कि आप उपclass नहीं कर सकते हैं? – ysth

+0

ऑब्जेक्ट्स में विधियां नहीं हैं, कक्षाओं में विधियां हैं। – ikegami

+1

@ikegami: यह आपके मॉडल पर निर्भर करता है। आप सभी * वास्तव में * परवाह है कि एक उदाहरण में सभी वर्ग हैं जो इसकी कक्षा द्वारा निर्दिष्ट हैं। रूबी में दोनों वर्ग विधियां हैं (जिन्हें केवल एक वर्ग पर लागू किया जा सकता है, उदाहरण पर नहीं) और उदाहरण विधियों, जिन्हें कक्षा में परिभाषित किया गया है लेकिन केवल उस वर्ग के उदाहरणों के माध्यम से उपलब्ध हैं। इसमें * सिंगलटन विधियां भी हैं जो एक दिए गए उदाहरण के लिए अद्वितीय हैं, जो इस ओपी की आवश्यकता है। – 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; 

    ... 
} 

ध्यान दें कि कि सबरूटीन के अंदर किसी भी वैश्विक आसपास के पैकेज में हो जाएगा, नहीं कुछ :: क्लास में। यदि आप निरंतर चर को subroutine के अंदर state या subroutine के बाहर 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 { ... }); 

पर्ल में तरीके एक वर्ग के साथ जुड़े हुए हैं, वस्तु नहीं। एक ऑब्जेक्ट को एक विधि असाइन करने के लिए, आपको उस ऑब्जेक्ट को अपनी कक्षा में रखना होगा।उपर्युक्त के समान, लेकिन आपको प्रत्येक उदाहरण के लिए उप-वर्ग बनाना होगा।

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 से बचा जाना चाहिए।

क्या आप सबक्लास, प्रतिनिधिमंडल या भूमिका का उपयोग करके वही काम पूरा कर सकते हैं?

पहले से ही पर्ल ओओ सिस्टम मौजूद हैं जो आपके लिए यह और बहुत कुछ करेंगे। आपको एक का उपयोग करना चाहिए। Moose, म्यू (Role::Tiny के माध्यम से) और Mouse सभी एक उदाहरण में भूमिकाएं जोड़ सकते हैं।

+0

मुझे इस बात पर विश्वास करना है कि यह उन असाधारण मामलों में से एक है क्योंकि 15 साल के पर्ले लड़के ने मुझे इस तरह से काम करने के तरीके के बजाय ऐसा करने के लिए कहा था ... मूल रूप से समस्या हल करने के बजाय ... :(हालांकि, अब मैं '* {$ class। ':: add_me'} = \ & add_me चलाता हूं; 'मुझे त्रुटि मिलती है:' स्ट्रिंग ('MYCLASSNAME" ...) का उपयोग प्रतीक संकेत के रूप में नहीं कर सकता है जबकि "सख्त रेफरी" उपयोग में है। (जहां MYCLASSNAME $ वर्ग का मान है) – Rooster

+0

पुन "किसी ऑब्जेक्ट को विधि निर्दिष्ट करने के लिए, आपको उस ऑब्जेक्ट को अपनी कक्षा में रखना होगा", या एक ऑटोलोडर का उपयोग करें। 'उप ऑटोडॉइड {...; मेरा $ method = $ self -> {विधियां} -> {$ method_name} या मरें ...; $ self -> $ विधि (@_);} ' – ikegami

+0

@ikegami whoops .... आह, संपादन को नहीं देखा (या शायद पूरी तरह से पढ़ा नहीं है)। अब कोशिश करें। – Rooster

 संबंधित मुद्दे