अपने सबसे हाल के लेख में, मैंने रेल 5.1, delegate_missing_to
में एक बेहतरीन नई सुविधा का उल्लेख किया है . delegate_missing_to
के साथ , कोई भी विधि जो आपको एक वस्तु पर नहीं मिल पाती है, उसे दूसरी वस्तु पर बुलाया जाता है, इसके बजाय :
class Player
delegate_missing_to :@user
def initalize(user)
@user = user
end
def points
Game.points_for_user(user.id)
end
end
Player.new(user).name # calls user.name
लेकिन, जैसा कि गेविन ने टिप्पणियों में उल्लेख किया है, यह विरासत से बचने का एक अजीब तरीका लगता है। क्यों न सिर्फ उपवर्ग का उपयोग करें? आपको वही प्रभाव मिलेगा, और आपको एक पूरी नई सुविधा जोड़ने की ज़रूरत नहीं है। यह जोड़ने के लिए एक अजीब चीज की तरह लगता है।
कोई कारण होना चाहिए delegate_missing_to
हालांकि जोड़ा गया था। और रेल सुविधाओं के लिए, पुल अनुरोध उन कारणों को खोजने का एक शानदार तरीका है। इस पुल अनुरोध में, DHH ने उल्लेख किया कि उसने इस सुविधा का सुझाव क्यों दिया:
अगर आप डेकोरेटर बनाना चाहते हैं तो यहां एक सामान्य पैटर्न दिया गया है:
यह खुदाई शुरू करने के लिए बहुत अच्छी जगह लगती है।
सज्जाकार क्यों?
जब आप एक डेकोरेटर बनाते हैं, तो आप एक नया उपवर्ग बनाए बिना किसी वस्तु के कार्य करने के तरीके को बदल रहे होते हैं। उदाहरण के लिए, पहले के कोड में:
class Player
delegate_missing_to :@user
def initalize(user)
@user = user
end
def points
Game.points_for_user(user.id)
end
end
आप कहेंगे कि "खिलाड़ी सजाता है उपयोगकर्ता," क्योंकि एक खिलाड़ी लगभग एक उपयोगकर्ता की तरह कार्य करता है, लेकिन एक अतिरिक्त विधि है, points
. और यह बिना इनहेरिटेंस के करता है।
आपको ऐसा कुछ क्यों चाहिए? इसका उत्तर देना एक कठिन प्रश्न है, क्योंकि कई डिज़ाइन पैटर्न की तरह, यह हमेशा स्पष्ट नहीं होता है कि आप किसी अन्य चीज़ के बजाय इसका उपयोग कहाँ करना चाहते हैं।
आप डेकोरेटर का उपयोग कब करेंगे?
सज्जाकार विरासत करने का एक और अधिक जटिल तरीका हो सकता है। मेरा मतलब है, कोड की इन दो पंक्तियों में से कौन सा बेहतर है?
player = Player.new(User.new(name: "Justin")) # Player decorates User
player = Player.new(name: "Justin") # Player subclasses User
स्पष्ट रूप से दूसरा, है ना? यहां, प्लेयर को उपवर्ग के बजाय डेकोरेटर के रूप में बनाना केवल कोड की बर्बादी है।
लेकिन कभी-कभी, आप बाद में किसी ऑब्जेक्ट में कार्यक्षमता जोड़ना चाहते हैं, जहां से आपने ऑब्जेक्ट बनाया है। उदाहरण के लिए, अगर आपके पास इस तरह का कोड होता तो क्या होता?
user = User.find(1)
... some time later ...
player = Player.new(user)
अब, आप अपनी इच्छानुसार उपयोगकर्ता बना सकते हैं, जिस भी तरीके से आप चाहें। उपयोगकर्ता ऑब्जेक्ट बनाने वाला कोड यह नहीं जानता या परवाह नहीं करता है कि एक प्लेयर क्लास भी मौजूद है। और यदि आप उन अतिरिक्त विधियों को अब और नहीं चाहते हैं तो भी आप मूल उपयोगकर्ता ऑब्जेक्ट का उपयोग कर सकते हैं।
इससे आपको व्यवहार को विभिन्न वर्गों में अलग करने में मदद मिलती है। प्रत्येक वर्ग इस बात पर ध्यान केंद्रित कर सकता है कि किसी विशिष्ट स्थिति में उपयोगकर्ता ऑब्जेक्ट का उपयोग कैसे किया जाएगा - एक खिलाड़ी, एक कर्मचारी, एक डेवलपर। इनहेरिटेंस के साथ, इन सभी चीज़ों को एक साथ मिलाना आसान है।
श्री क्रिस ने टिप्पणियों में सज्जाकारों के लिए एक और लाभ का उल्लेख किया:
जब आप किसी वस्तु को सजाते हैं, तो आप उस वस्तु पर केवल सार्वजनिक विधियों को ही कॉल कर सकते हैं। जब आप सबक्लास करते हैं, तो आप किसी भी विधि को कॉल कर सकते हैं, यहां तक कि निजी भी। इससे उपवर्ग अधिक बार टूट सकते हैं, क्योंकि वे गलती से अपने माता-पिता के कार्यान्वयन विवरण पर भरोसा कर सकते हैं। वे विवरण आम तौर पर सार्वजनिक तरीकों की तुलना में अधिक बार बदलते हैं।
जब आप बड़ी कक्षाओं को तोड़ रहे हों तो सज्जाकार विशेष रूप से उपयोगी हो सकते हैं। डेकोरेटर के साथ, सिंगल-रिस्पॉन्सिबिलिटी सिद्धांत का पालन करना आसान होता है - प्रत्येक डेकोरेटर एक काम कर सकता है और इसे अच्छी तरह से कर सकता है, और आप अधिक जटिल व्यवहार प्राप्त करने के लिए डेकोरेटर्स को जोड़ सकते हैं।
रूबी में व्यवहार साझा करने के कई तरीके हैं। आप उपवर्ग कर सकते हैं, आप मॉड्यूल को जोड़ सकते हैं, आप एक वर्ग से विधियों को भी पकड़ सकते हैं और यदि आप चाहें तो उन्हें दूसरे से जोड़ सकते हैं। हालाँकि, डेकोरेटर पैटर्न आपको कुछ अलग देता है। आप केवल आवृत्ति चर और विधि कॉल का उपयोग कर रहे हैं, जो किसी भी वस्तु-उन्मुख भाषा के निर्माण खंड हैं। उन बुनियादी बातों से, आपका ऐप चलने के दौरान लचीला व्यवहार हो सकता है - सब कुछ आपके कोड को अधिक जटिल किए बिना।