2015-12-30 3 views
7

मान लें करने के लिए विधि जोड़ने मैं एक वर्ग है:रूबी एक वर्ग

class Foo 
end 

इस वर्ग के लिए एक विधि को जोड़ने के लिए मैं 2 विकल्प पता:

  1. वर्ग को पुन: खोलने और विधि को लागू:

    class Foo 
        def bar 
        end 
    end 
    
  2. विधि को लागू करने के class_eval का उपयोग कर:

    +०१२३५१६४१०
    Foo.class_eval { def bar; end} 
    

क्या अंतर है? इनमे से कौन बेहतर है?

+0

[ 'फू # define_method'] (http://ruby-doc.org/core-2.2.0/Module.html#method-i-define_method) भी। – mudasobwa

उत्तर

15

असल में, कक्षा में नए तरीकों को जोड़ने के कुछ अन्य तरीके हैं। उदाहरण के लिए, आप मॉड्यूल में विधियों को भी परिभाषित कर सकते हैं, और मॉड्यूल को मूल वर्ग में मिला सकते हैं।

module ExtraMethods 
    def bar 
    end 
end 

Foo.class_eval { include ExtraMethods } 
class Foo 
    include ExtraMethods 
end 

कोई वास्तविक या बुरा नहीं है। आपके द्वारा वर्णित दो (या तीन) तरीकों में अलग-अलग व्यवहार हैं और आप अपनी आवश्यकता (या वरीयता) के आधार पर एक या दूसरे का उपयोग करना चाह सकते हैं। ज्यादातर मामलों में, यह व्यक्तिपरक है। अन्य मामलों में, यह वास्तव में इस पर निर्भर करता है कि आपका कोड कैसे संरचित किया गया है।

class_eval का उपयोग कर वर्ग बनाम फिर से खोलने के बीच मुख्य उद्देश्य अंतर यह है कि पहला व्यक्ति वर्ग परिभाषा भी है, जबकि दूसरे को मूल वर्ग को पहले ही परिभाषित करने की आवश्यकता है।

अभ्यास में, कुछ मामलों में कक्षा को फिर से खोलने से कुछ अप्रत्याशित दुष्प्रभाव हो सकते हैं। मान लीजिए कि आपने Foo को lib/foo.rb फ़ाइल में विधियों के समूह के साथ परिभाषित किया है। फिर आप को config/initializers/extra.rb में फिर से खोलें और आप bar विधि जोड़ें।

myclass.rb में आप Foo का उपयोग करते हैं, लेकिन lib/foo.rb मैन्युअल रूप से आवश्यक होने की बजाय, आप एक ऑटोलोड लोड पर भरोसा करते हैं।

extra.rblib/foo.rb से पहले भरी हुई है, तो क्या हो सकता है कि Foo वर्ग पहले से ही अपने वातावरण में परिभाषित किया गया है, और अपने कोड lib/foo.rb लोड नहीं होगा है। आपके पास Foo क्लास होगा जो आपके द्वारा परिभाषित bar एक्सटेंशन है, और मूल Foo एक नहीं है।

दूसरे शब्दों में, यदि किसी भी कारण से आप क्लास को फिर से खोलने के लिए कुछ तरीकों को जोड़ने के लिए पूर्ण उत्पत्ति वर्ग परिभाषा को पहले लोड किया गया है (या उसके बाद), तो यह कोड टूट सकता है अगर यह ऑटोलोड पर निर्भर करता है।

इसके विपरीत, Foo.class_evalFoo पर एक विधि कॉल करता है, इसलिए यह नई विधियों को जोड़ने का प्रयास करते समय मूल Foo परिभाषा पहले से मौजूद होने की अपेक्षा करता है। यह सुनिश्चित करता है कि, जब आप नई विधियां जोड़ते हैं, तो Foo कक्षा पहले ही परिभाषित की जाएगी।

निष्कर्ष में, मुख्य अंतर यह है कि कक्षा को दोबारा खोलने से आपको कक्षा में विधियों को जोड़ने की अनुमति मिलती है जो अभी तक लोड नहीं हो सकते हैं, जबकि class_eval कक्षा को पहले ही परिभाषित करने की आवश्यकता है।

सामान्य रूप से, जब तक कि मैं नामित उप-वर्गों को परिभाषित नहीं कर रहा हूं या कक्षाओं को दोबारा खोल रहा हूं, मेरा पूरा नियंत्रण है, मैं दूसरे कोड को पसंद करता हूं क्योंकि बड़े कोडबेस में यह कोड को और अधिक रखरखाव करता है। असल में, मैं आमतौर पर मिश्रित का उपयोग करता हूं यदि मैं तीसरे पक्ष के वर्गों का विस्तार करता हूं ताकि अगर मैं मौजूदा तरीकों को ओवरराइड करने की आवश्यकता हो तो मैं पूर्ण विधि पूर्वजों श्रृंखला को बनाए रख सकता हूं।

+0

कोई "रूबी ऑटोलोड" नहीं है, तो शायद आपको "रेल ऑटोलोड" का अर्थ है। – mudasobwa

+0

@ मुदासोबावा तकनीकी रूप से ('ऑटोलोड' विधि है), लेकिन यह इस मामले पर लागू नहीं होगा। मैंने जवाब में एक छोटा सा बदलाव किया। इस पर ध्यान दिलाने के लिए धन्यवाद। –

5

दूसरा दृष्टिकोण बहुत आसान है जब आपको कुछ गतिशील चीज़ की आवश्यकता होती है।

# scope one, opened with `class` keyword 
class ... 
    # scope two, opened with `def` keyword 
    def ... 
    end 
end 

class_eval के साथ, आप कार्यक्षेत्र साझा कर सकते हैं: रूबी वास्तव में कई स्कोप है।

>> foo = 1 
=> 1 
>> class Foo 
>> puts foo 
>> def bar 
>>  puts foo 
>> end 
>> end 
NameError: undefined local variable or method 'foo' for Foo:Class 
     from (irb):3:in <class:Foo> 
     from (irb):2 
>> Foo 
=> Foo 
>> Foo.class_eval { 
?> puts foo 
>> define_method :bar do 
>>  puts foo 
>> end 
>> } 
1 
=> :bar 
>> Foo.new.bar 
1 
+1

लेकिन इसका यह भी अर्थ है कि यह साझा क्षेत्र कचरा नहीं होगा जबकि कक्षा मौजूद है – Vasfed

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