2012-04-26 13 views
25

मैंने देखा है वहाँ बंदर को दो सामान्य तरीके रूबी में एक वर्ग पैच हैं कि:अनुशंसित दृष्टिकोण रूबी में एक वर्ग पैचिंग

तो जैसे वर्ग पर नए सदस्यों को परिभाषित करें:

class Array 
    def new_method 
    #do stuff 
    end 
end 

और वर्ग वस्तु पर class_eval बुला:

Array.class_eval do 
    def new_method 
     #do stuff 
    end 
end 

मैं अगर वहाँ दो और है कि क्या वहाँ एक दूसरे के ऊपर दृष्टिकोण का उपयोग करने के लिए फायदे हैं के बीच कोई अंतर है सोच रहा हूँ?

+0

की संभावित डुप्लिकेट [बंदर पैचिंग बनाम वर्ग \ _eval?] (Http://stackoverflow.com/questions/9399358/monkey-patching-vs-class-eval) – akostadinov

उत्तर

54

ईमानदारी से, मैं पहले फॉर्म (कक्षा को फिर से खोलने) का उपयोग करता था, क्योंकि यह अधिक प्राकृतिक लगता है, लेकिन आपके प्रश्न ने मुझे इस विषय पर कुछ शोध करने के लिए मजबूर किया और यहां परिणाम है।

कक्षा को फिर से खोलने में समस्या यह है कि मूल रूप से एक नई कक्षा को परिभाषित कर दिया जाएगा, जिसे आप फिर से खोलना चाहते हैं, क्योंकि किसी कारण से इस समय परिभाषित नहीं किया गया था। परिणाम अलग हो सकता है:

  1. आप किसी भी तरीकों पर हावी नहीं है, लेकिन केवल नए लोगों को जोड़ने और मूल कार्यान्वयन परिभाषित किया गया है (उदाहरण के लिए, फ़ाइल, जहां वर्ग मूल रूप से परिभाषित किया गया है भरी हुई है) बाद में सब कुछ होगा ठीक हो।

  2. यदि आप कुछ विधियों को फिर से परिभाषित करते हैं और मूल बाद में लोड हो जाता है तो आपके तरीकों को उनके मूल संस्करणों के साथ वापस ओवरराइड कर दिया जाएगा।

  3. सबसे दिलचस्प मामला तब होता है जब आप कक्षा 0 लोड/पुनः लोड करने के लिए मानक autoloading या कुछ फैंसी रीलोडिंग तंत्र (जैसे रेल में उपयोग किए गए) का उपयोग करते हैं। इनमें से कुछ समाधान const_missing पर भरोसा करते हैं जिन्हें आप अनिर्धारित निरंतर संदर्भित करते हैं। उस स्थिति में ऑटोलोडिंग तंत्र अपरिभाषित वर्ग की परिभाषा को खोजने और इसे लोड करने का प्रयास करता है। लेकिन यदि आप अपनी खुद की कक्षा को परिभाषित कर रहे हैं (जबकि आप पहले से परिभाषित एक को फिर से खोलने का इरादा रखते हैं) तो यह अब 'लापता' नहीं होगा और मूल लोड कभी भी लोड नहीं किया जाएगा क्योंकि ऑटोलोडिंग तंत्र ट्रिगर नहीं किया जाएगा।

दूसरी ओर, यदि आप का उपयोग class_eval आप तुरंत सूचित किया जाएगा यदि वर्ग पल में परिभाषित नहीं है। इसके अतिरिक्त, जब आप अपनी class_eval विधि को कॉल करते हैं तो कक्षा को संदर्भित कर रहे हैं, तो किसी भी ऑटोलोडिंग तंत्र को कक्षा की परिभाषा का पता लगाने और इसे लोड करने का मौका मिलेगा।

यह ध्यान में रखते हुए class_eval एक बेहतर दृष्टिकोण प्रतीत होता है। हालांकि, मुझे कुछ और राय सुनने में खुशी होगी।

+0

अच्छा अनुसंधान :) –

+0

गूगल एक है सभी के बाद सुंदर शक्तिशाली उपकरण =) –

6

स्कोप

एक बड़ा अंतर मुझे लगता है कि,, KL-7 का कहना नहीं था गुंजाइश है, जिसमें अपने नए कोड में व्याख्या की जाएगी है:

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

CONSTANT = 'surrounding scope' 

# original class definition (uses class scope) 
class C 
    CONSTANT = 'class scope' 

    def fun() p CONSTANT end 
end 
C.new.fun # prints: "class scope" 


# monkey-patching with #class_eval: uses surrounding scope! 
C.class_eval do 
    def fun() p CONSTANT end 
end 
C.new.fun # prints: "surrounding scope" 


# monkey-patching by re-opening the class: uses scope of class C 
class C 
    def fun() p CONSTANT end 
end 
C.new.fun # prints: "class scope" 
संबंधित मुद्दे