"वंशानुक्रम पर पसंदीदा रचना" सिर्फ एक अच्छा अनुमान है
आपको संदर्भ पर विचार करना चाहिए, क्योंकि यह एक सार्वभौमिक नियम नहीं है। इसका मतलब यह नहीं है कि जब आप रचना कर सकते हैं तो विरासत का उपयोग कभी न करें। अगर ऐसा होता, तो आप विरासत में प्रतिबंध लगाकर इसे ठीक कर देते।
मैं इस पोस्ट के साथ इस बिंदु को स्पष्ट करने की उम्मीद करता हूं।
मैं स्वयं रचना के गुणों का बचाव करने की कोशिश नहीं करूंगा। जिसे मैं टॉपिक मानता हूं। इसके बजाय, मैं कुछ स्थितियों के बारे में बात करूंगा जब डेवलपर विरासत पर विचार कर सकता है जो रचना का उपयोग करना बेहतर होगा। उस नोट पर, विरासत की अपनी खूबियां हैं, जिन्हें मैं विषय से अलग मानता हूं।
वाहन का उदाहरण
मैं उन डेवलपर्स के बारे में लिख रहा हूं जो कथा उद्देश्यों के लिए गूंगे तरीके से चीजों की कोशिश करते हैं
कि कुछ OOP पाठ्यक्रम का उपयोग हमें शास्त्रीय उदाहरण का एक संस्करण के लिए चलते हैं ... हम एक है Vehicle, वर्ग तो हम निकाले जाते हैं Car, Airplane, Balloonऔर Ship।
नोट : यदि आपको इस उदाहरण को आधार बनाने की आवश्यकता है, तो दिखावा करें कि ये वीडियो गेम में वस्तुओं के प्रकार हैं।
तब Carऔर Airplaneकुछ सामान्य कोड हो सकते हैं, क्योंकि वे दोनों जमीन पर पहिया पर रोल कर सकते हैं। डेवलपर्स इसके लिए वंशानुक्रम श्रृंखला में एक मध्यस्थ वर्ग बनाने पर विचार कर सकते हैं। फिर भी, वास्तव में Airplaneऔर के बीच कुछ साझा कोड भी है Balloon। वे इसके लिए विरासत श्रृंखला में एक और मध्यस्थ वर्ग बनाने पर विचार कर सकते हैं।
इस प्रकार, डेवलपर कई उत्तराधिकार की तलाश में होगा। उस बिंदु पर जहां डेवलपर्स कई विरासत की तलाश कर रहे हैं, डिजाइन पहले ही गलत हो गया है।
इस व्यवहार को इंटरफेस और रचना के रूप में मॉडल करना बेहतर है, इसलिए हम इसे कई वर्ग उत्तराधिकार में चलाने के बिना पुन: उपयोग कर सकते हैं। यदि डेवलपर्स, उदाहरण के लिए, FlyingVehiculeवर्ग बनाते हैं । वे कह रहे हैं कि Airplaneयह एक FlyingVehicule(श्रेणी विरासत) है, लेकिन हम इसके बजाय यह कह सकते हैं कि Airplaneएक Flyingघटक (रचना) है और Airplaneएक IFlyingVehicule(इंटरफ़ेस विरासत) है।
इंटरफेस का उपयोग करना, यदि आवश्यक हो, तो हमारे पास कई विरासत (इंटरफेस के) हो सकते हैं। इसके अलावा, आप किसी विशेष कार्यान्वयन के लिए युग्मन नहीं कर रहे हैं। अपने कोड की पुन: प्रयोज्यता और परीक्षणशीलता बढ़ाना।
याद रखें कि वंशानुक्रम बहुरूपता के लिए एक उपकरण है। इसके अलावा, बहुरूपता पुन: प्रयोज्य के लिए एक उपकरण है। यदि आप रचना का उपयोग करके अपने कोड की पुन: प्रयोज्यता बढ़ा सकते हैं, तो ऐसा करें। यदि आपको यकीन नहीं है कि जो कुछ भी या नहीं रचना बेहतर पुन: प्रयोज्यता प्रदान करती है, "विरासत पर वरीयता को प्राथमिकता दें" एक अच्छा विधर्मी है।
बिना जिक्र किए सब Amphibious।
वास्तव में, हमें उन चीजों की आवश्यकता नहीं हो सकती है जो जमीन से दूर जाती हैं। स्टीफन हर्न ने अपने लेख "एहसान रचना ओवर इनहेरिटेंस" भाग 1 और भाग 2 में अधिक स्पष्ट उदाहरण दिया है ।
सबस्टिबिलिटी और एनकैप्सुलेशन
Aवंशानुक्रम या रचना करनी चाहिए B?
यदि उस Aकी एक विशेषज्ञता Liskov प्रतिस्थापन सिद्धांतB को पूरा करना चाहिए , तो वंशानुक्रम व्यवहार्य है, यहां तक कि वांछनीय भी है। यदि ऐसी परिस्थितियां हैं, जिनके लिए एक वैध प्रतिस्थापन नहीं है, तो हमें विरासत का उपयोग नहीं करना चाहिए।AB
हमें व्युत्पन्न प्रोग्रामिंग के रूप में रचना में रुचि हो सकती है, व्युत्पन्न वर्ग की रक्षा करने के लिए । विशेष रूप से, एक बार जब आप Bअन्य विभिन्न उद्देश्यों के लिए उपयोग करना शुरू करते हैं, तो उन उद्देश्यों के लिए अधिक उपयुक्त होने के लिए इसे बदलने या विस्तारित करने का दबाव हो सकता है। यदि ऐसा जोखिम है जो Bउन तरीकों को उजागर कर सकता है, जिनके परिणामस्वरूप Aहम अमान्य स्थिति में हो सकते हैं, तो हमें विरासत के बजाय रचना का उपयोग करना चाहिए। यहां तक कि अगर हम दोनों के लेखक हैं Bऔर Aयह चिंता करने के लिए कम है, इसलिए रचना की पुनरावृत्ति को कम करता है B।
हम भी तर्क दे सकता है कि देखते हैं अगर में सुविधाओं Bहै कि Aजरूरत नहीं है (और अगर उन सुविधाओं के लिए एक अमान्य स्थिति में हो सकता है हम नहीं जानते A, या तो वर्तमान कार्यान्वयन में या भविष्य में), यह उपयोग संरचना के लिए एक अच्छा विचार है विरासत के बदले।
कंपोज़िशन में स्विचिंग इंप्लीमेंटेशन की अनुमति देने और मॉकिंग को आसान बनाने के फायदे भी हैं।
नोट : ऐसी परिस्थितियाँ हैं जहाँ हम प्रतिस्थापन मान्य होने के बावजूद रचना का उपयोग करना चाहते हैं। हम इंटरफ़ेस या अमूर्त कक्षाओं (जो कि किसी अन्य विषय का उपयोग करने के लिए) का उपयोग करके प्रतिस्थापन योग्य है, और फिर वास्तविक कार्यान्वयन की निर्भरता इंजेक्शन के साथ रचना का उपयोग करते हैं।
अंत में, निश्चित रूप से, यह तर्क है कि हमें मूल वर्ग का बचाव करने के लिए रचना का उपयोग करना चाहिए क्योंकि वंशानुक्रम मूल वर्ग के इनकैप्सुलेशन को तोड़ता है:
वंशानुक्रम अपने माता-पिता के कार्यान्वयन के विवरण के लिए एक उपवर्ग को उजागर करता है, यह अक्सर कहा जाता है कि 'वंशानुक्रम विच्छेद को तोड़ता है'
- डिजाइन पैटर्न: पुन: प्रयोज्य वस्तु-उन्मुख सॉफ्टवेयर के तत्व, गैंग ऑफ़ फोर
ठीक है, यह एक खराब डिजाइन वाला मूल वर्ग है। यही कारण है कि तुम चाहिए:
वंशानुक्रम के लिए डिज़ाइन करें, या इसे प्रतिबंधित करें।
- प्रभावी जावा, जोश बलोच
यो-यो समस्या
एक और मामला जहां रचना मदद करती है वह है यो-यो समस्या । यह विकिपीडिया का एक उद्धरण है:
सॉफ्टवेयर विकास में, यो-यो समस्या एक एंटी-पैटर्न है जो तब होती है जब एक प्रोग्रामर को एक प्रोग्राम को पढ़ना और समझना होता है जिसका वंशानुक्रम ग्राफ इतना लंबा और जटिल होता है कि प्रोग्रामर को कई अलग-अलग वर्ग परिभाषाओं के बीच फ़्लिप करते रहना पड़ता है ताकि उसे फॉलो किया जा सके कार्यक्रम का नियंत्रण प्रवाह।
आप उदाहरण के लिए हल कर सकते हैं: आपकी कक्षा कक्षा Cसे विरासत में नहीं आएगी B। इसके बजाय आपकी कक्षा Cमें एक प्रकार का सदस्य होगा A, जो किसी प्रकार की वस्तु हो सकती है (या हो सकती है) B। इस तरह से आप कार्यान्वयन विवरण के विरुद्ध प्रोग्रामिंग नहीं करेंगे B, लेकिन उस अनुबंध के विरुद्ध जो इंटरफ़ेस () का Aप्रस्ताव देता है।
काउंटर उदाहरण
कई चौखटे रचना के ऊपर वंशानुक्रम का पक्ष लेते हैं (जो हम बहस कर रहे हैं उसके विपरीत है)। डेवलपर ऐसा कर सकता है क्योंकि उन्होंने अपने बेस क्लास में बहुत सारे काम किए हैं, जो इसे कंपोजिशन के साथ लागू करते हैं, जिससे क्लाइंट कोड का आकार बढ़ जाएगा। कभी-कभी यह भाषा की सीमाओं के कारण होता है।
उदाहरण के लिए, एक PHP ओआरएम फ्रेमवर्क एक बेस क्लास बना सकता है जो जादू कोड का उपयोग करता है ताकि कोड लिखने की अनुमति दे सके जैसे कि ऑब्जेक्ट में वास्तविक गुण थे। इसके बजाय मैजिक मेथड्स द्वारा संभाला गया कोड डेटाबेस में जाएगा, विशेष रूप से अनुरोधित फ़ील्ड के लिए क्वेरी (शायद भविष्य के अनुरोध के लिए इसे कैश करें) और इसे वापस लौटा दें। रचना के साथ ऐसा करने से ग्राहक को प्रत्येक क्षेत्र के लिए गुण बनाने या जादू के तरीकों के कोड के कुछ संस्करण लिखने की आवश्यकता होगी।
परिशिष्ट : कुछ अन्य तरीके हैं जो ओआरएम वस्तुओं का विस्तार कर सकते हैं। इस प्रकार, मुझे नहीं लगता कि इस मामले में विरासत जरूरी है। यह सस्ता है।
एक अन्य उदाहरण के लिए, एक वीडियो गेम इंजन एक बेस क्लास बना सकता है जो 3 डी रेंडरिंग और इवेंट हैंडलिंग करने के लिए लक्ष्य प्लेटफॉर्म के आधार पर मूल कोड का उपयोग करता है। यह कोड जटिल और मंच विशिष्ट है। इस कोड से निपटने के लिए इंजन के डेवलपर उपयोगकर्ता के लिए यह महंगा और त्रुटि प्रवण होगा, वास्तव में, यह इंजन का उपयोग करने के कारण का हिस्सा है।
इसके अलावा, 3 डी प्रतिपादन भाग के बिना, यह कई विजेट फ्रेमवर्क कैसे काम करता है। यह आपको ओएस संदेशों को संभालने की चिंता से मुक्त करता है ... वास्तव में, कई भाषाओं में आप देशी कोडिंग के कुछ फार्म के बिना ऐसा कोड नहीं लिख सकते हैं। इसके अलावा, यदि आप इसे करते हैं, तो यह आपकी पोर्टेबिलिटी को तंग करेगा। इसके बजाय, विरासत के साथ, बशर्ते कि डेवलपर संगतता (बहुत अधिक) को न तोड़े; आप भविष्य में किसी भी नए प्लेटफ़ॉर्म का समर्थन करने के लिए आसानी से अपने कोड को पोर्ट कर पाएंगे।
इसके अलावा, विचार करें कि कई बार हम केवल कुछ तरीकों को ओवरराइड करना चाहते हैं और डिफ़ॉल्ट कार्यान्वयन के साथ बाकी सब कुछ छोड़ देते हैं। यदि हम रचना का उपयोग कर रहे थे तो हमें उन सभी तरीकों को बनाना होगा, भले ही केवल लपेटी गई वस्तु को सौंपना हो।
इस तर्क के अनुसार, एक ऐसा बिंदु है जहां रचना विरासत के आधार पर बनाए रखने के लिए सबसे खराब हो सकती है (जब आधार वर्ग बहुत जटिल है)। फिर भी, याद रखें कि विरासत की स्थिरता रचना की तुलना में बदतर हो सकती है (जब विरासत पेड़ बहुत जटिल है), जो कि मैं यो-यो समस्या का उल्लेख करता हूं।
प्रस्तुत उदाहरणों में, डेवलपर्स शायद ही कभी अन्य परियोजनाओं में विरासत के माध्यम से उत्पन्न कोड का पुन: उपयोग करने का इरादा रखते हैं। यह रचना के बजाय वंशानुक्रम का उपयोग करने की कम पुन: प्रयोज्यता को कम करता है। इसके अलावा, इनहेरिटेंस का उपयोग करके फ्रेमवर्क डेवलपर्स कोड का उपयोग करने के लिए बहुत आसान और आसान खोज प्रदान कर सकते हैं।
अंतिम विचार
जैसा कि आप देख सकते हैं, रचना को कुछ स्थितियों में विरासत पर कुछ फायदा होता है, हमेशा नहीं। निर्णय लेने के लिए संदर्भ और शामिल विभिन्न कारकों (जैसे पुन: प्रयोज्य, रखरखाव, परीक्षणशीलता, आदि) पर विचार करना महत्वपूर्ण है। पहला बिंदु पर वापस "विरासत से अधिक रचना पसंद करते हैं" एक है सिर्फ अच्छा अनुमानी।
आपके पास यह भी नोटिस हो सकता है कि मेरे द्वारा बताई गई कई स्थितियों को कुछ हद तक ट्रिट्स या मिक्सिन्स के साथ हल किया जा सकता है। अफसोस की बात है कि भाषाओं की महान सूची में ये सामान्य विशेषताएं नहीं हैं, और वे आमतौर पर कुछ प्रदर्शन लागत के साथ आते हैं। शुक्र है, उनके लोकप्रिय चचेरे भाई एक्सटेंशन तरीके और डिफ़ॉल्ट कार्यान्वयन कुछ स्थितियों को कम करते हैं।
मेरे पास एक हालिया पोस्ट है जहां मैं इंटरफेस के कुछ फायदों के बारे में बात करता हूं कि हमें सी # में यूआई, बिजनेस और डेटा एक्सेस के बीच इंटरफेस की आवश्यकता क्यों है । यह decoupling में मदद करता है और पुन: प्रयोज्यता और परीक्षणशीलता को आसान बनाता है, आपको रुचि हो सकती है।