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