वंशानुक्रम पर रचना को प्राथमिकता दें?


1603

वंशानुक्रम पर रचना क्यों पसंद करते हैं? प्रत्येक दृष्टिकोण के लिए क्या व्यापार-नापसंद हैं? आपको रचना पर विरासत का चयन कब करना चाहिए?



40
यहाँ उस प्रश्न के बारे में एक अच्छा लेख है । मेरी निजी राय है कि डिजाइन करने के लिए कोई "बेहतर" या "बदतर" सिद्धांत नहीं है। कंक्रीट कार्य के लिए "उपयुक्त" और "अपर्याप्त" डिजाइन है। दूसरे शब्दों में - मैं स्थिति के आधार पर विरासत या रचना दोनों का उपयोग करता हूं। लक्ष्य छोटे कोड का उत्पादन, पढ़ने में आसान, पुन: उपयोग और अंततः आगे का विस्तार करना है।
m_pGladiator

1
एक वाक्य में विरासत सार्वजनिक है यदि आपके पास एक सार्वजनिक तरीका है और आप इसे बदलते हैं तो यह प्रकाशित एपीआई को बदल देता है। यदि आपके पास रचना है और रचना की गई वस्तु बदल गई है तो आपको अपने प्रकाशित एपीआई को बदलने की आवश्यकता नहीं है।
तोमर बेन डेविड

जवाबों:


1188

इनहेरिटेंस पर रचना को प्राथमिकता दें क्योंकि यह बाद में संशोधित करने के लिए अधिक निंदनीय / आसान है, लेकिन हमेशा कंपोज-एप्रोच का उपयोग न करें। रचना के साथ, डिपेंडेंसी इंजेक्शन / सेटर्स के साथ मक्खी पर व्यवहार को बदलना आसान है। विरासत अधिक कठोर है क्योंकि अधिकांश भाषाएं आपको एक से अधिक प्रकार से प्राप्त करने की अनुमति नहीं देती हैं। एक बार जब आप टाइपा से निकलते हैं, तो हंस कम या ज्यादा पकाया जाता है।

उपरोक्त के लिए मेरा एसिड परीक्षण है:

  • क्या TypeB पूर्ण प्रकार के इंटरफ़ेस (सभी सार्वजनिक विधियों से कम नहीं) को टाइप करना चाहता है, जिससे TypeB का उपयोग किया जा सकता है जहाँ TypeA अपेक्षित है? वंशानुक्रम का संकेत देता है ।

    • उदाहरण के लिए, ए सेस्ना बीप्लैन एक हवाई जहाज के पूर्ण इंटरफ़ेस को उजागर करेगा, यदि अधिक नहीं। ताकि यह हवाई जहाज से उतरने लायक हो जाए।
  • क्या TypeB केवल TypeA द्वारा उजागर किए गए व्यवहार का कुछ / हिस्सा चाहता है? संकेत के लिए रचना की आवश्यकता है

    • उदाहरण के लिए ए बर्ड को केवल एक हवाई जहाज के उड़ान व्यवहार की आवश्यकता हो सकती है। इस मामले में, इसे एक इंटरफ़ेस / वर्ग / दोनों के रूप में निकालने और इसे दोनों वर्गों का सदस्य बनाने के लिए समझ में आता है।

अद्यतन: बस मेरे जवाब पर वापस आया और अब ऐसा लगता है कि यह बारबरा लिस्कॉव के लिस्कोव प्रतिस्थापन सिद्धांत के एक विशिष्ट उल्लेख के बिना अधूरा है 'क्या मुझे इस प्रकार से विरासत में मिलना चाहिए?'


81
दूसरा उदाहरण सीधे हेड फ़र्स्ट डिज़ाइन पैटर्न ( amazon.com/First-Design-Patterns-Elisabeth-Freeman/dp/… ) की किताब से बाहर है :) मैं इस प्रश्न को समझने वाले किसी भी व्यक्ति को वह पुस्तक सुझाऊंगा।
जेशुरुन

4
यह बहुत स्पष्ट है, लेकिन यह कुछ याद कर सकता है: "क्या टाइपबीए टाइप के पूर्ण इंटरफ़ेस (सभी सार्वजनिक तरीके कम नहीं) को उजागर करना चाहता है ताकि टाइपबी का उपयोग किया जा सके जहां टाइपा की उम्मीद है?" लेकिन क्या होगा अगर यह सच है, और TypeB भी TypeC के पूर्ण इंटरफ़ेस को उजागर करता है? और क्या होगा यदि TypeC अभी तक मॉडलिंग नहीं की गई है?
ट्रिस्टन

4
आप जो मुझे लगता है कि सबसे बुनियादी परीक्षण होना चाहिए के लिए allude: "क्या यह वस्तु कोड द्वारा प्रयोग करने योग्य होनी चाहिए जो आधार प्रकार की वस्तुओं (क्या होगा) की अपेक्षा करता है"। यदि उत्तर हां है, तो वस्तु को विरासत में प्राप्त करना चाहिए । यदि नहीं, तो यह शायद नहीं करना चाहिए। यदि मेरे पास मेरे शराबी होते हैं, तो भाषाएं "इस वर्ग" को संदर्भित करने के लिए एक कीवर्ड प्रदान करेंगी, और एक कक्षा को परिभाषित करने का एक साधन प्रदान करें, जो अन्य वर्ग की तरह ही व्यवहार करे, लेकिन इसके लिए प्रतिस्थापित नहीं किया जाना चाहिए (ऐसी कक्षा में यह सब होगा) वर्ग "संदर्भ स्वयं के साथ प्रतिस्थापित)।
सुपरकैट

22
@ एसेक्स - बिंदु 'क्या मैं उन सभी ग्राहकों के लिए एक सेसना बीप्लैन में पास कर सकता हूं जो बिना किसी आश्चर्य के एक हवाई जहाज की उम्मीद करते हैं?' यदि हाँ, तो संभावना है कि आप विरासत चाहते हैं।
गिशु

9
मैं वास्तव में किसी भी उदाहरण के बारे में सोचने के लिए संघर्ष कर रहा हूं जहां विरासत मेरा जवाब होगा, मैं अक्सर एकत्रीकरण, रचना और इंटरफेस का परिणाम और अधिक सुरुचिपूर्ण समाधानों में पाता हूं। उपरोक्त उदाहरणों में से कई को संभवतः उन दृष्टिकोणों का उपयोग करके बेहतर ढंग से समझाया जा सकता है ...
स्टुअर्ट वेकफील्ड

414

एक के रूप में होने के बारे में सोचो एक है संबंध के संबंध के । एक कार "में एक" इंजन, एक व्यक्ति "का एक" नाम आदि होता है।

एक रिश्ते के रूप में विरासत के बारे में सोचो । एक कार "एक" वाहन, एक व्यक्ति "एक" स्तनपायी, आदि है।

मैं इस दृष्टिकोण का कोई श्रेय नहीं लेता। मैंने इसे सीधे स्टीव मैककोनेल , धारा 6.3 द्वारा कोड के दूसरे संस्करण से लिया ।


107
यह हमेशा एक सही दृष्टिकोण नहीं है, यह केवल एक अच्छा दिशानिर्देश है। Liskov प्रतिस्थापन सिद्धांत अधिक सटीक है (कम विफल रहता है)।
बिल के

40
"मेरी कार में एक वाहन है।" यदि आप मानते हैं कि एक अलग वाक्य के रूप में, प्रोग्रामिंग संदर्भ में नहीं, तो यह बिल्कुल समझ में नहीं आता है। और यही इस तकनीक का पूरा बिंदु है। अगर यह अजीब लगता है, तो शायद गलत है।
निक ज़ालुत्सिकी

36
@ निक ज़रूर, लेकिन "मेरी कार में एक व्हीकलबीवर है" अधिक समझ में आता है (मुझे लगता है कि आपके "वाहन" वर्ग का नाम "व्हीकलबहेवियर" हो सकता है)। इसलिए आप अपने निर्णय को आधार नहीं बना सकते "एक" बनाम "एक" तुलना है, आपको एलएसपी का उपयोग करना होगा, या आप गलतियां करेंगे
ट्रिस्टन

35
इसके बजाय "एक के बारे में सोच" जैसा व्यवहार है। वंशानुक्रम व्यवहार के बारे में है, न कि केवल शब्दार्थ।
यबकोस

4
@ybakos "व्यवहार की तरह" विरासत के लिए आवश्यकता के बिना इंटरफेस के माध्यम से प्राप्त किया जा सकता है। विकिपीडिया से : "वंशानुक्रम पर रचना का कार्यान्वयन आम तौर पर उन व्यव्हारों का प्रतिनिधित्व करने वाले विभिन्न इंटरफेस के निर्माण से शुरू होता है जिन्हें सिस्टम को प्रदर्शित करना चाहिए ... इस प्रकार, सिस्टम व्यवहार विरासत के बिना महसूस किया जाता है।"
डेविड आरआर

210

यदि आप अंतर को समझते हैं, तो समझाना आसान है।

प्रक्रियात्मक कोड

इसका एक उदाहरण कक्षाओं के उपयोग के बिना PHP है (विशेषकर PHP5 से पहले)। सभी तर्क कार्यों के एक सेट में एन्कोडेड है। आप सहायक कार्यों सहित अन्य फ़ाइलों को शामिल कर सकते हैं और फ़ंक्शंस में डेटा पास करके अपने व्यावसायिक तर्क का संचालन कर सकते हैं। एप्लिकेशन बढ़ने पर इसे प्रबंधित करना बहुत कठिन हो सकता है। PHP5 अधिक वस्तु उन्मुख डिजाइन की पेशकश करके इसे मापने की कोशिश करता है।

विरासत

यह कक्षाओं के उपयोग को प्रोत्साहित करता है। विरासत ओओ डिजाइन (विरासत, बहुरूपता, इनकैप्सुलेशन) के तीन सिद्धांतों में से एक है।

class Person {
   String Title;
   String Name;
   Int Age
}

class Employee : Person {
   Int Salary;
   String Title;
}

यह काम में विरासत है। कर्मचारी "एक व्यक्ति है या व्यक्ति से विरासत में मिला है।" सभी विरासत रिश्ते "एक-एक" रिश्ते हैं। कर्मचारी भी व्यक्ति से शीर्षक की संपत्ति को छीन लेता है, जिसका अर्थ है कर्मचारी। कर्मचारी कर्मचारी के लिए शीर्षक नहीं लौटाएगा।

रचना

रचना विरासत के पक्ष में है। इसे बहुत सरलता से रखने के लिए आपके पास होगा:

class Person {
   String Title;
   String Name;
   Int Age;

   public Person(String title, String name, String age) {
      this.Title = title;
      this.Name = name;
      this.Age = age;
   }

}

class Employee {
   Int Salary;
   private Person person;

   public Employee(Person p, Int salary) {
       this.person = p;
       this.Salary = salary;
   }
}

Person johnny = new Person ("Mr.", "John", 25);
Employee john = new Employee (johnny, 50000);

रचना आमतौर पर "एक" या "एक" संबंध का उपयोग करती है। यहां कर्मचारी वर्ग का एक व्यक्ति है। यह व्यक्ति से विरासत में नहीं मिलता है, बल्कि व्यक्तिगत वस्तु को पास कर देता है, यही कारण है कि इसमें एक "व्यक्ति" है।

वंशानुक्रम पर रचना

अब कहते हैं कि आप एक प्रबंधक प्रकार बनाना चाहते हैं ताकि आप समाप्त हो जाएं:

class Manager : Person, Employee {
   ...
}

यह उदाहरण ठीक काम करेगा, हालांकि, अगर व्यक्ति और कर्मचारी दोनों घोषित Titleहो तो क्या होगा ? प्रबंधक चाहिए। "परिचालन के प्रबंधक" या "श्री" लौटाएं? रचना के तहत इस अस्पष्टता को बेहतर तरीके से संभाला गया है:

Class Manager {
   public string Title;
   public Manager(Person p, Employee e)
   {
      this.Title = e.Title;
   }
}

प्रबंधक वस्तु को एक कर्मचारी और एक व्यक्ति के रूप में बनाया गया है। शीर्षक व्यवहार कर्मचारी से लिया जाता है। यह स्पष्ट रचना अन्य चीजों के बीच अस्पष्टता को दूर करती है और आप कम बग का सामना करेंगे।


6
विरासत के लिए: कोई अस्पष्टता नहीं है। आप आवश्यकताओं के आधार पर प्रबंधक वर्ग को लागू कर रहे हैं। यदि आप अपनी आवश्यकताओं को निर्दिष्ट करते हैं, तो आप "ऑपरेशन के प्रबंधक" को वापस करेंगे, अन्यथा आप बस बेस क्लास के कार्यान्वयन का उपयोग करेंगे। इसके अलावा, आप व्यक्ति को एक सार वर्ग बना सकते हैं और इस तरह यह सुनिश्चित कर सकते हैं कि डाउन-स्ट्रीम कक्षाएं एक शीर्षक संपत्ति को लागू करें।
राज राव

68
यह याद रखना महत्वपूर्ण है कि कोई "संरचना पर विरासत" कह सकता है लेकिन इसका मतलब यह नहीं है कि "समग्रता पर रचना हमेशा"। "ए" का अर्थ है विरासत और कोड का पुन: उपयोग होता है। कर्मचारी एक व्यक्ति है (कर्मचारी के पास एक व्यक्ति नहीं है)।
राज राव

36
उदाहरण भ्रमित है। कर्मचारी एक व्यक्ति है, इसलिए इसे विरासत का उपयोग करना चाहिए। आपको इस उदाहरण के लिए संरचना का उपयोग नहीं करना चाहिए, क्योंकि यह डोमेन मॉडल में गलत संबंध है, भले ही तकनीकी रूप से आप इसे कोड में घोषित कर सकते हैं।
माइकल फ्रीजिम

15
मैं इस उदाहरण से असहमत हूं। एक कर्मचारी एक व्यक्ति है, जो कि विरासत के उचित उपयोग का एक पाठ्यपुस्तक मामला है। मुझे यह भी लगता है कि शीर्षक क्षेत्र के पुनर्वितरण के "मुद्दे" का कोई मतलब नहीं है। तथ्य यह है कि Employee.Title shadows Person.Title खराब प्रोग्रामिंग का संकेत है। आखिरकार, "मि।" और "ऑपरेशन के प्रबंधक" वास्तव में एक व्यक्ति (लोअरकेस) के एक ही पहलू का जिक्र करते हैं? मैं Employee.Title का नाम बदल दूंगा, और इस प्रकार एक कर्मचारी के शीर्षक और JobTitle विशेषताओं को संदर्भित करने में सक्षम हो सकता हूं, दोनों वास्तविक जीवन में समझ में आते हैं। इसके अलावा, प्रबंधक के लिए कोई कारण नहीं है (जारी ...)
रेडन रोसबोरो

9
(… जारी रखा) व्यक्ति और कर्मचारी दोनों से विरासत में मिला - आखिरकार, कर्मचारी पहले से ही व्यक्ति से विरासत में मिला है। अधिक जटिल मॉडल में, जहां एक व्यक्ति एक प्रबंधक और एक एजेंट हो सकता है, यह सच है कि एकाधिक वंशानुक्रम का उपयोग किया जा सकता है (ध्यान से!), लेकिन यह कई वातावरणों में बेहतर होगा जिसमें प्रबंधक से एक अमूर्त भूमिका वर्ग हो (जिसमें कर्मचारी शामिल हों) s / he प्रबंधित करता है) और Agent (अनुबंध, और अन्य जानकारी समाहित करता है) इनहेरिट करता है। फिर, एक कर्मचारी एक व्यक्ति है, जिसके पास कई-कई रोल्स हैं। इस प्रकार, रचना और विरासत दोनों का उपयोग ठीक से किया जाता है।
रेडॉन रोसबोरो

141

विरासत द्वारा प्रदान किए गए सभी निर्विवाद लाभों के साथ, यहां इसके कुछ नुकसान हैं।

वंशानुक्रम के नुकसान:

  1. आप सुपरटाइम से विरासत में प्राप्त कार्यान्वयन को नहीं बदल सकते हैं (जाहिर है क्योंकि विरासत का संकलन समय पर परिभाषित किया गया है)।
  2. वंशानुक्रम अपने मूल वर्ग के कार्यान्वयन के विवरण के लिए एक उपवर्ग को उजागर करता है, इसीलिए अक्सर यह कहा जाता है कि वंशानुक्रम में अतिक्रमण टूटता है (इस अर्थ में कि आपको वास्तव में केवल क्रियान्वयन नहीं करने वाले इंटरफेस पर ध्यान देने की आवश्यकता है, इसलिए उप-वर्ग द्वारा पुन: उपयोग करना हमेशा पसंद नहीं किया जाता है)।
  3. विरासत द्वारा प्रदान की गई टाइट कपलिंग एक सुपर क्लास के कार्यान्वयन के साथ एक उपवर्ग के कार्यान्वयन को बहुत बाध्य बनाती है कि माता-पिता के कार्यान्वयन में कोई भी बदलाव उप वर्ग को बदलने के लिए मजबूर करेगा।
  4. सब-क्लासिंग द्वारा अत्यधिक पुन: उपयोग इनहेरिटेंस स्टैक को बहुत गहरा और बहुत भ्रमित कर सकता है।

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


5
मेरी राय में, यह बेहतर उत्तरों में से एक है - मैं इसे इस बात से जोड़ूंगा कि रचना की दृष्टि से आपकी समस्याओं पर फिर से विचार करने की कोशिश, मेरे अनुभव में, छोटे, सरल, अधिक आत्म-निहित, अधिक पुन: प्रयोज्य वर्गों की ओर ले जाती है। एक स्पष्ट, छोटे, जिम्मेदारी के अधिक केंद्रित दायरे के साथ। अक्सर इसका मतलब यह है कि निर्भरता इंजेक्शन या मॉकिंग (परीक्षणों में) जैसी चीजों की कम आवश्यकता होती है क्योंकि छोटे घटक आमतौर पर अपने दम पर खड़े होने में सक्षम होते हैं। बस मेरा अनुभव। YMMV :-)
mindplay.dk

3
इस पोस्ट में अंतिम पैराग्राफ वास्तव में मेरे लिए क्लिक किया। धन्यवाद।
सालक्स

87

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


81

जबकि कम शब्दों में मैं "उत्तराधिकार पर रचना को प्राथमिकता देता हूं" से सहमत होता हूं, बहुत बार मेरे लिए ऐसा लगता है कि "कोका-कोला पर आलू पसंद करते हैं"। वंशानुक्रम के लिए स्थान हैं और रचना के लिए स्थान हैं। आपको अंतर समझने की आवश्यकता है, फिर यह प्रश्न गायब हो जाएगा। मेरे लिए वास्तव में इसका मतलब है "यदि आप विरासत का उपयोग करने जा रहे हैं - फिर से सोचें, संभावना है कि आपको रचना की आवश्यकता है"।

जब आप खाना चाहते हैं, तब आपको कोका कोला के ऊपर आलू को प्राथमिकता देनी चाहिए और जब आप पीना चाहें तो कोका कोला को आलू के ऊपर डाल दें।

सुपरक्लास विधियों को कॉल करने के लिए केवल एक सुविधाजनक तरीका से अधिक उपवर्ग बनाना चाहिए। जब सबक्लास है तो आपको इनहेरिटेंस का उपयोग करना चाहिए "एक" सुपर क्लास दोनों संरचनात्मक और कार्यात्मक रूप से, जब इसे सुपरक्लास के रूप में इस्तेमाल किया जा सकता है और आप इसका उपयोग करने जा रहे हैं। यदि यह मामला नहीं है - यह विरासत नहीं है, लेकिन कुछ और है। रचना तब होती है जब आपकी वस्तुओं में कोई दूसरा होता है, या उनसे कोई संबंध होता है।

इसलिए मेरे लिए ऐसा लगता है कि अगर किसी को नहीं पता कि उसे विरासत या रचना की जरूरत है, तो असली समस्या यह है कि वह नहीं जानता कि वह पीना चाहता है या खाना। अपने समस्या डोमेन के बारे में अधिक सोचें, इसे बेहतर समझें।


5
सही काम के लिए सही उपकरण। एक हथौड़ा एक रिंच की तुलना में चीजों को तेज़ करने में बेहतर हो सकता है, लेकिन इसका मतलब यह नहीं है कि एक रिंच को "हीन हथौड़ा" के रूप में देखना चाहिए। जब वस्तु को सुपरक्लास वस्तु के रूप में व्यवहार करने के लिए उपवर्ग में जोड़ा जाता है, तो विरासत में मदद मिल सकती है। उदाहरण के लिए, InternalCombustionEngineएक व्युत्पन्न वर्ग के साथ एक आधार वर्ग पर विचार करें GasolineEngine। उत्तरार्द्ध स्पार्क प्लग जैसी चीजों को जोड़ता है, जिसमें बेस क्लास का अभाव है, लेकिन इस चीज का उपयोग एक InternalCombustionEngineस्पार्क प्लग के रूप में किया जाएगा।
15

61

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

विरासत शायद युग्मन का सबसे खराब रूप है जो आपके पास हो सकता है

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

लेख, इनहेरिटेंस इविल: द एपिक फेल ऑफ द डेटानोटेशनमॉडेलबाइंडर , सी # में इस के एक उदाहरण के माध्यम से चलता है। यह विरासत के उपयोग को दर्शाता है जब रचना का उपयोग किया जाना चाहिए था और इसे फिर से कैसे बनाया जा सकता था।


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

42

जावा या C # में, एक वस्तु तुरंत टाइप होने के बाद उसका प्रकार नहीं बदल सकती है।

इसलिए, यदि आपकी वस्तु को एक अलग वस्तु के रूप में प्रकट होने की आवश्यकता है या किसी वस्तु स्थिति या स्थितियों के आधार पर अलग तरह से व्यवहार करना है, तो संरचना का उपयोग करें : देखें राज्य और रणनीति का डिजाइन पैटर्न का ।

यदि ऑब्जेक्ट को एक ही प्रकार का होना चाहिए, तो इनहेरिटेंस का उपयोग करें या इंटरफेस को लागू करें।


10
+1 मैंने कम और कम पाया है कि वंशानुक्रम अधिकांश स्थितियों में काम करता है। मैं ज्यादा साझा / विरासत में मिला हुआ इंटरफेस और वस्तुओं की रचना पसंद करता हूं .... या इसे एकत्रीकरण कहा जाता है? मुझसे मत पूछो, मुझे EE की डिग्री मिली है !!
केनी

मेरा मानना ​​है कि यह सबसे आम परिदृश्य है जहां "विरासत पर रचना" लागू होती है क्योंकि दोनों सिद्धांत रूप में उपयुक्त हो सकते हैं। उदाहरण के लिए, एक विपणन प्रणाली में आप की अवधारणा हो सकती है Client। फिर, PreferredClientबाद में एक चबूतरे की एक नई अवधारणा । PreferredClientवंशानुक्रम होना चाहिए Client? एक पसंदीदा ग्राहक 'ग्राहक' है, नहीं? खैर, इतनी जल्दी नहीं ... जैसा कि आपने कहा कि ऑब्जेक्ट्स रनटाइम पर अपनी क्लास नहीं बदल सकते। आप client.makePreferred()ऑपरेशन को कैसे मॉडल करेंगे ? शायद जवाब एक लापता अवधारणा के साथ रचना का उपयोग करने में निहित है, एक Accountशायद?
plalx

विभिन्न प्रकार की Clientकक्षाएं होने के बजाय , शायद वहाँ सिर्फ एक है जो एक की अवधारणा को AccountStandardAccountPreferredAccount
एनकैप्सुलेट

40

यहाँ संतोषजनक उत्तर नहीं मिला, इसलिए मैंने एक नया लिखा।

यह समझने के लिए कि " वंशानुक्रम पर रचना को क्यों पसंद करते हैं ", हमें पहले इस संक्षिप्त मुहावरे में छोड़ी गई धारणा को वापस लाने की आवश्यकता है।

वंशानुक्रम के दो लाभ हैं: उपप्रकार और उपवर्ग

  1. सबटाइपिंग का अर्थ है एक प्रकार (इंटरफ़ेस) हस्ताक्षर, यानी एपीआई का एक सेट के अनुरूप, और एक व्यक्ति बहुरूपता को प्राप्त करने के लिए हस्ताक्षर के भाग को ओवरराइड कर सकता है।

  2. सबक्लासिंग का अर्थ है विधि के कार्यान्वयन का निहित पुन: उपयोग।

दो लाभों के साथ विरासत करने के लिए दो अलग-अलग उद्देश्य आते हैं: उन्मुखीकरण और कोड का पुन: उपयोग करना।

यदि कोड का पुन: उपयोग एकमात्र उद्देश्य है, तो उप-वर्ग एक से अधिक दे सकता है जो उसे चाहिए, अर्थात माता-पिता वर्ग के कुछ सार्वजनिक तरीके बच्चे की कक्षा के लिए बहुत मायने नहीं रखते हैं। इस मामले में, विरासत पर रचना का पक्ष लेने के बजाय, रचना की मांग की जाती है । यह भी है जहाँ "is-a" बनाम "has-a" धारणा आती है।

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

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

  1. वंशानुक्रम सीधे ओवरड्यूड कोड प्रदान करता है यदि ओवरराइड नहीं किया जाता है, जबकि रचना को हर एपीआई को फिर से कोड करना पड़ता है, भले ही यह प्रतिनिधिमंडल का एक सरल काम हो।

  2. इनहेरिटेंस आंतरिक पॉलीमॉर्फिक साइट के माध्यम से सीधा खुला पुनरावृत्ति प्रदान करता है this, अर्थात किसी अन्य सदस्य फ़ंक्शन में ओवरराइडिंग विधि (या यहां तक ​​कि प्रकार ) को सार्वजनिक या निजी (हालांकि हतोत्साहित )। खुली पुनरावृत्ति को रचना के माध्यम से अनुकरण किया जा सकता है , लेकिन इसके लिए अतिरिक्त प्रयास की आवश्यकता होती है और हमेशा व्यवहार्य (?) नहीं हो सकती है। नकल वाले सवाल का यह जवाब कुछ इसी तरह की बात करता है।

  3. वंशानुक्रम संरक्षित सदस्यों को उजागर करता है । इससे अभिभावक वर्ग का एनकैप्सुलेशन टूट जाता है, और यदि उपवर्ग द्वारा उपयोग किया जाता है, तो बच्चे और उसके माता-पिता के बीच एक और निर्भरता पेश की जाती है।

  4. संरचना में नियंत्रण के उलटा होने की संभावना है, और इसकी निर्भरता को गतिशील रूप से इंजेक्ट किया जा सकता है, जैसा कि डेकोरेटर पैटर्न और प्रॉक्सी पैटर्न में दिखाया गया है ।

  5. कॉम्बीनेटर-ओरिएंटेड प्रोग्रामिंग का लाभ कंपोजिट है , अर्थात समग्र पैटर्न की तरह काम करना ।

  6. रचना तुरंत एक अंतरफलक के लिए प्रोग्रामिंग का अनुसरण करती है

  7. रचना में आसान कई विरासत का लाभ है ।

मन में उपरोक्त व्यापार के उतार के साथ, हम इसलिए विरासत पर रचना पसंद करते हैं । फिर भी कसकर संबंधित वर्गों के लिए, अर्थात जब निहित कोड का पुन: उपयोग वास्तव में लाभ पहुंचाता है, या खुली पुनरावृत्ति की जादू शक्ति वांछित है, तो विरासत का विकल्प होगा।


34

व्यक्तिगत रूप से मैंने हमेशा विरासत पर रचना को प्राथमिकता देना सीखा। कोई प्रोग्रामेटिक समस्या नहीं है जिसे आप विरासत के साथ हल कर सकते हैं जिसे आप रचना के साथ हल नहीं कर सकते हैं; हालाँकि आपको कुछ मामलों में Interfaces (Java) या प्रोटोकॉल (Obj-C) का उपयोग करना पड़ सकता है। चूंकि C ++ ऐसी किसी भी चीज़ को नहीं जानता है, इसलिए आपको अमूर्त आधार वर्गों का उपयोग करना होगा, जिसका अर्थ है कि आप C ++ में विरासत से पूरी तरह छुटकारा नहीं पा सकते हैं।

रचना अक्सर अधिक तार्किक होती है, यह बेहतर अमूर्तता प्रदान करती है, बेहतर एनकैप्सुलेशन, बेहतर कोड का पुन: उपयोग (विशेषकर बहुत बड़ी परियोजनाओं में) और कुछ दूरी पर कुछ भी टूटने की संभावना कम होती है क्योंकि आपने अपने कोड में कहीं भी एक अलग परिवर्तन किया है। यह " सिंगल रिस्पॉन्सिबिलिटी प्रिंसिपल " को बनाए रखने में भी आसान बनाता है , जिसे अक्सर संक्षेप में " एक वर्ग को बदलने के लिए एक से अधिक कारण नहीं होना चाहिए। ", और इसका मतलब है कि प्रत्येक वर्ग एक विशिष्ट उद्देश्य के लिए मौजूद है और यह केवल ऐसे तरीके हैं जो सीधे इसके उद्देश्य से संबंधित हैं। इसके अलावा एक बहुत उथले विरासत पेड़ होने के कारण अवलोकन को बनाए रखना बहुत आसान हो जाता है जब आपकी परियोजना वास्तव में बड़ी होने लगती है। बहुत से लोग सोचते हैं कि विरासत हमारी वास्तविक दुनिया का प्रतिनिधित्व करती हैबहुत अच्छी तरह से, लेकिन यह सच नहीं है। वास्तविक दुनिया विरासत की तुलना में बहुत अधिक संरचना का उपयोग करती है। बहुत अधिक हर वास्तविक विश्व वस्तु जिसे आप अपने हाथ में पकड़ सकते हैं, अन्य छोटी दुनिया की वस्तुओं से बना है।

हालांकि रचना के डाउनसाइड्स हैं। यदि आप विरासत को पूरी तरह से छोड़ देते हैं और केवल रचना पर ध्यान केंद्रित करते हैं, तो आप देखेंगे कि आपको अक्सर अतिरिक्त कोड लाइनों के एक जोड़े को लिखना होगा जो कि यदि आपने विरासत का उपयोग किया था तो आवश्यक नहीं थे। आपको कभी-कभी खुद को दोहराने के लिए भी मजबूर किया जाता है और इसका उल्लंघन होता है DRY सिद्धांत(DRY = खुद को न दोहराएं)। इसके अलावा रचना को अक्सर प्रतिनिधिमंडल की आवश्यकता होती है, और एक विधि किसी अन्य ऑब्जेक्ट की एक अन्य विधि को बुला रही है, जिसमें इस कोड के आसपास कोई अन्य कोड नहीं है। इस तरह के "डबल मेथड कॉल्स" (जो आसानी से ट्रिपल या चौगुनी मेथड कॉल्स और उससे भी आगे तक बढ़ सकते हैं) में इनहेरिटेंस की तुलना में बहुत बुरा प्रदर्शन होता है, जहाँ आपको बस अपने माता-पिता की एक विधि विरासत में मिलती है। विरासत में मिली विधि को कॉल करना गैर-विरासत वाले को कॉल करने के समान तेज़ हो सकता है, या यह थोड़ा धीमा हो सकता है, लेकिन आमतौर पर दो लगातार विधि कॉलों की तुलना में तेजी से होता है।

आपने देखा होगा कि अधिकांश OO भाषाएँ एकाधिक वंशानुक्रम की अनुमति नहीं देती हैं। हालांकि ऐसे कुछ मामले हैं जहां कई विरासत वास्तव में आपको कुछ खरीद सकते हैं, लेकिन वे नियम के बजाय अपवाद हैं। जब भी आप ऐसी स्थिति में दौड़ते हैं, जहां आपको लगता है कि "कई विरासत इस समस्या को हल करने के लिए एक बहुत अच्छी सुविधा होगी", तो आप आमतौर पर एक ऐसे बिंदु पर होते हैं, जहां आपको विरासत के बारे में पूरी तरह से सोचना चाहिए, क्योंकि यहां तक ​​कि इसके लिए अतिरिक्त कोड लाइनों की एक जोड़ी की आवश्यकता हो सकती है , रचना पर आधारित एक समाधान आमतौर पर अधिक सुरुचिपूर्ण, लचीला और भविष्य के प्रमाण के रूप में सामने आएगा।

वंशानुक्रम वास्तव में एक शांत विशेषता है, लेकिन मुझे डर है कि यह पिछले कुछ वर्षों में अति प्रयोग किया गया है। लोगों ने विरासत का इलाज किया एक हथौड़ा के रूप में जो यह सब कील कर सकता है, भले ही यह वास्तव में एक नाखून, एक पेंच या शायद पूरी तरह से अलग हो।


"बहुत से लोग सोचते हैं कि विरासत हमारी वास्तविक दुनिया का अच्छी तरह से प्रतिनिधित्व करती है, लेकिन यह सच्चाई नहीं है।" इतना तो! कभी दुनिया में सभी प्रोग्रामिंग ट्यूटोरियल के विपरीत, वास्तविक दुनिया की वस्तुओं को विरासत चेन के रूप में मॉडलिंग करना संभवतः लंबे समय में एक बुरा विचार है। आपको विरासत का उपयोग केवल तभी करना चाहिए जब एक अविश्वसनीय रूप से स्पष्ट, सहज, सरल रिश्ता हो। जैसे TextFileहै a File
neonblitzer

25

अंगूठे का मेरा सामान्य नियम: विरासत का उपयोग करने से पहले, विचार करें कि क्या रचना अधिक समझ में आती है।

कारण: उपवर्ग का मतलब आमतौर पर अधिक जटिलता और जुड़ाव होता है, यानी गलतियाँ करने के बिना बदलना, बनाए रखना, और पैमाना।

टिम बोउद्रेउ ऑफ़ सन से एक बहुत अधिक पूर्ण और ठोस उत्तर :

मैं देख रहा हूँ के रूप में विरासत के उपयोग के लिए आम समस्याओं:

  • निर्दोष कृत्यों के अनपेक्षित परिणाम हो सकते हैं - इसका उत्कृष्ट उदाहरण सुपरक्लास कंस्ट्रक्टर से अतिप्रयोग योग्य तरीकों के लिए कॉल है, इससे पहले कि उप-वर्ग उदाहरण क्षेत्रों को आरम्भ किया गया है। एक आदर्श दुनिया में, कोई भी ऐसा कभी नहीं करेगा। यह एक आदर्श दुनिया नहीं है।
  • यह विधि कॉल के क्रम के बारे में धारणा बनाने के लिए उपवर्गों के लिए व्यापक प्रलोभन प्रदान करता है और इस तरह की - ऐसी धारणाएं स्थिर नहीं होती हैं यदि सुपरक्लास समय के साथ विकसित हो सकता है। मेरे टोस्टर और कॉफी पॉट सादृश्य भी देखें ।
  • कक्षाएं भारी हो जाती हैं - आप जरूरी नहीं जानते हैं कि आपका सुपरक्लास अपने कंस्ट्रक्टर में क्या काम कर रहा है, या यह कितनी मेमोरी का उपयोग करने वाला है। तो कुछ निर्दोष हो सकता है-हो सकता है कि हल्के वस्तु आप की तुलना में कहीं अधिक महंगा हो सकता है, और यह समय के साथ बदल सकता है अगर सुपरस्टार घूमा करता है
  • यह उपवर्गों के विस्फोट को प्रोत्साहित करता है । क्लास लोडिंग में समय लगता है, अधिक कक्षाओं में मेमोरी खर्च होती है। यह तब तक एक गैर-मुद्दा हो सकता है जब तक आप नेटबीन्स के पैमाने पर एक ऐप के साथ काम नहीं कर रहे हैं, लेकिन वहां, हमारे पास वास्तविक मुद्दे थे, उदाहरण के लिए, मेनू धीमा हो रहा है क्योंकि मेनू के पहले प्रदर्शन ने बड़े पैमाने पर क्लास लोडिंग चालू कर दिया। हमने इसे और अधिक घोषणात्मक सिंटैक्स और अन्य तकनीकों में स्थानांतरित करके तय किया, लेकिन उस लागत को भी ठीक करने के लिए।
  • बाद में चीजों को बदलना कठिन हो जाता है - यदि आपने एक वर्ग को सार्वजनिक कर दिया है, तो सुपरक्लास को स्वैप करके उपवर्गों को तोड़ने जा रहा है - यह एक विकल्प है, जो एक बार कोड को सार्वजनिक करने के बाद, आपसे शादी कर लेता है। इसलिए यदि आप अपने सुपरक्लास में वास्तविक कार्यक्षमता में परिवर्तन नहीं कर रहे हैं, तो आपको बाद में यदि आप उपयोग करते हैं, तो अपनी ज़रूरत की चीज़ों को बढ़ाने के बजाय चीजों को बदलने की अधिक स्वतंत्रता मिलती है। उदाहरण के लिए, JPanel को उपवर्ग में लेना - यह आमतौर पर गलत है; और अगर उपवर्ग कहीं सार्वजनिक है, तो आपको कभी भी उस फैसले को दोबारा करने का मौका नहीं मिलता है। यदि इसे JComponent getThePanel () के रूप में एक्सेस किया जाता है, तो आप अभी भी इसे कर सकते हैं (संकेत: अपने एपीआई के रूप में घटकों के लिए मॉडल का पर्दाफाश करें)।
  • ऑब्जेक्ट पदानुक्रम स्केल नहीं करते हैं (या बाद में उन्हें स्केल करना आगे की योजना बनाने की तुलना में बहुत कठिन है) - यह क्लासिक "बहुत सारी परतें" समस्या है। मैं इसके नीचे जाता हूँ, और AskTheOracle पैटर्न इसे कैसे हल कर सकता है (हालाँकि यह OOP शुद्धतावादियों को नाराज कर सकता है)।

...

मेरा क्या करना है, यदि आप विरासत के लिए अनुमति देते हैं, जो आप नमक के दाने के साथ ले सकते हैं:

  • स्थिरांक को छोड़कर, कभी भी कोई भी क्षेत्र उजागर न करें
  • विधियाँ या तो अमूर्त या अंतिम होंगी
  • सुपरक्लास कंस्ट्रक्टर से कोई विधि न कहें

...

यह सब बड़े लोगों की तुलना में छोटी परियोजनाओं के लिए, और सार्वजनिक लोगों की तुलना में निजी कक्षाओं के लिए कम लागू होता है


25

वंशानुक्रम पर रचना क्यों पसंद करते हैं?

अन्य उत्तर देखें।

आप विरासत का उपयोग कब कर सकते हैं?

यह अक्सर कहा जाता है कि एक वर्ग एक वर्ग Barको विरासत में दे सकता है Fooजब निम्नलिखित वाक्य सत्य होता है:

  1. एक बार एक फू है

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

  1. एक बार एक फू है, और
  2. बार वो सबकुछ कर सकता है जो फॉक्स कर सकता है।

पहला परीक्षण सुनिश्चित सब ही टिककर खेल की Fooमें कर भावना Bar(= साझा गुण) है, जबकि दूसरे टेस्ट सुनिश्चित करती है कि सभी setters की Fooमें कर भावना Bar(= साझा कार्यक्षमता)।

उदाहरण 1: कुत्ता -> पशु

कुत्ता एक जानवर है और कुत्ते वह सब कुछ कर सकते हैं जो जानवर कर सकते हैं (जैसे कि साँस लेना, मरना, आदि)। इसलिए, वर्ग वर्ग Dog को विरासत में दे सकता हैAnimal

उदाहरण 2: वृत्त - / -> दीर्घवृत्त

एक वृत्त एक दीर्घवृत्त है, लेकिन वृत्त वह सब कुछ नहीं कर सकता जो दीर्घवृत्त कर सकता है। उदाहरण के लिए, वृत्त नहीं खींच सकते, जबकि दीर्घवृत्त हो सकते हैं। इसलिए, वर्ग वर्ग Circle को इनहेरिट नहीं कर सकता हैEllipse

इसे सर्कल-एलिप्से समस्या कहा जाता है , जो वास्तव में एक समस्या नहीं है, बस एक स्पष्ट प्रमाण है कि अकेले पहला परीक्षण पर्याप्त नहीं है जो यह निष्कर्ष निकाल सके कि विरासत संभव है। विशेष रूप से, यह उदाहरण हाइलाइट किया गया है कि व्युत्पन्न वर्ग को बेस कक्षाओं की कार्यक्षमता का विस्तार करना चाहिए , इसे कभी भी प्रतिबंधित नहीं करना चाहिए । अन्यथा, बेस क्लास का उपयोग पॉलीमॉर्फिक रूप से नहीं किया जा सकता है।

आपको वंशानुक्रम का उपयोग कब करना चाहिए?

यहां तक ​​कि अगर आप विरासत का उपयोग कर सकते हैं, तो इसका मतलब यह नहीं है कि आपको रचना का उपयोग करना हमेशा एक विकल्प होता है। वंशानुक्रम एक शक्तिशाली उपकरण है जो निहित कोड का पुन: उपयोग और गतिशील प्रेषण की अनुमति देता है, लेकिन यह कुछ नुकसान के साथ आता है, यही वजह है कि रचना अक्सर पसंद की जाती है। विरासत और रचना के बीच का व्यापार- उतार स्पष्ट नहीं है, और मेरी राय में lcn के उत्तर में सबसे अच्छा समझाया गया है ।

अंगूठे के एक नियम के रूप में, मैं रचना पर विरासत का चयन करता हूं, जब बहुरूपिक उपयोग बहुत सामान्य होने की उम्मीद होती है, इस मामले में गतिशील प्रेषण की शक्ति बहुत अधिक पठनीय और सुरुचिपूर्ण एपीआई हो सकती है। उदाहरण के लिए, WidgetGUI चौखटे में एक बहुरूपी वर्ग , या NodeXML पुस्तकालयों में एक बहुरूपी वर्ग होने की अनुमति देता है एक एपीआई जो बहुत अधिक पठनीय और सहज ज्ञान युक्त उपयोग की तुलना में क्या आप शुद्ध रूप से रचना के आधार पर होगा।

लिसकोव प्रतिस्थापन सिद्धांत

जैसा कि आप जानते हैं, यह निर्धारित करने के लिए उपयोग की जाने वाली एक अन्य विधि कि वंशानुक्रम संभव है, लिस्कोव प्रतिस्थापन सिद्धांत कहलाता है :

आधार वर्गों के लिए संकेत या संदर्भ का उपयोग करने वाले कार्य यह जानने के बिना व्युत्पन्न वर्गों की वस्तुओं का उपयोग करने में सक्षम होना चाहिए

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


वृत्त-दीर्घवृत्त और वर्ग-आयत परिदृश्य खराब उदाहरण हैं। उप-वर्ग अपने सुपरक्लास की तुलना में हमेशा अधिक जटिल होते हैं, इसलिए समस्या का सामना करना पड़ता है। रिश्ते को उलझाकर इस समस्या को हल किया जाता है। एक दीर्घवृत्त एक वृत्त से निकलता है और एक आयत एक वर्ग से निकलता है। इन परिदृश्यों में रचना का उपयोग करना बेहद मूर्खतापूर्ण है।
फजी लॉजिक

@ फजीलोगिक सहमत, लेकिन वास्तव में, मेरी पोस्ट इस मामले में रचना का उपयोग करने की वकालत कभी नहीं करती। मैंने केवल यह कहा है कि सर्कल-दीर्घवृत्त समस्या इसका एक बड़ा उदाहरण है कि "is-a" अकेले यह निष्कर्ष निकालने के लिए एक अच्छा परीक्षण नहीं है कि सर्किल को एलीप से निकलना चाहिए। एक बार जब हम यह निष्कर्ष निकाल लेते हैं कि एलएसपी के उल्लंघन के कारण सर्किल को एलीप से प्राप्त नहीं करना चाहिए, तो संभावित विकल्प रिश्ते को पलटना, या रचना का उपयोग करना, या टेम्पलेट कक्षाओं का उपयोग करना, या अतिरिक्त कक्षाओं या सहायक कार्यों से युक्त अधिक जटिल डिज़ाइन का उपयोग करना है, आदि ... और निर्णय स्पष्ट रूप से केस-बाय-केस आधार पर लिया जाना चाहिए।
बोरिस डालस्टीन

1
@ फजीलोगिक और यदि आप इस बारे में उत्सुक हैं कि मैं सर्कल-एलिपसे के विशिष्ट मामले की वकालत क्या करूंगा: मैं सर्कल वर्ग को लागू नहीं करने की वकालत करूंगा। रिश्ते को प्रभावित करने वाला मुद्दा यह है कि यह एलएसपी का भी उल्लंघन करता है: फ़ंक्शन की कल्पना करें computeArea(Circle* c) { return pi * square(c->radius()); }। यह स्पष्ट रूप से टूटा हुआ है अगर एक एलीप्स (क्या त्रिज्या () भी मतलब है?) पारित कर दिया। एक दीर्घवृत्त एक सर्कल नहीं है, और जैसे सर्कल से व्युत्पन्न नहीं होना चाहिए।
बोरिस डालस्टीन

computeArea(Circle *c) { return pi * width * height / 4.0; }अब यह सामान्य है।
फजी लॉजिक

2
@FuzzyLogic मैं असहमत हूं: आप महसूस करते हैं कि इसका मतलब है कि वर्ग सर्कल ने व्युत्पन्न वर्ग एलीप के अस्तित्व का अनुमान लगाया है, और इसलिए प्रदान किया गया है width()और height()? क्या होगा अगर अब एक लाइब्रेरी उपयोगकर्ता "एगशैप" नामक एक अन्य वर्ग बनाने का फैसला करता है? क्या इसे "सर्कल" से भी प्राप्त किया जाना चाहिए? बिलकूल नही। एक अंडा-आकार एक सर्कल नहीं है, और एक अंडाकार भी एक सर्कल नहीं है, इसलिए किसी को भी सर्कल से प्राप्त नहीं करना चाहिए क्योंकि यह एलएसपी है। एक सर्कल * वर्ग पर ऑपरेशन करने वाले तरीके एक सर्कल के बारे में मजबूत धारणा बनाते हैं, और इन धारणाओं को तोड़ने से लगभग निश्चित रूप से कीड़े पैदा होंगे।
बोरिस डालस्टीन

19

वंशानुक्रम बहुत शक्तिशाली है, लेकिन आप इसे मजबूर नहीं कर सकते हैं (देखें: सर्कल-दीर्घवृत्त समस्या )। यदि आप वास्तव में पूरी तरह से एक सच्चे के बारे में सुनिश्चित नहीं हो सकते हैं "- एक" उपप्रकार संबंध है, तो रचना के साथ जाना सबसे अच्छा है।


15

वंशानुक्रम एक उपवर्ग और सुपर क्लास के बीच एक मजबूत संबंध बनाता है; उपवर्ग को सुपर क्लास के कार्यान्वयन विवरण के बारे में पता होना चाहिए। सुपर क्लास बनाना बहुत कठिन है, जब आपको यह सोचना होगा कि इसे कैसे बढ़ाया जा सकता है। आपको वर्ग के आक्रमणकारियों को सावधानीपूर्वक दस्तावेज़ करना होगा, और यह बताना होगा कि अन्य तरीकों से अधिक उपयोग करने योग्य तरीके आंतरिक रूप से क्या हैं।

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

वंशानुक्रम आसानी से अधिक उपयोग किया जाता है, हालांकि, और कक्षाओं के बीच कठिन निर्भरता के साथ, अतिरिक्त जटिलता पैदा करता है। यह भी समझना कि किसी प्रोग्राम के निष्पादन के दौरान क्या होता है, लेयर और मेथड कॉल के गतिशील चयन के कारण बहुत कठिन हो जाता है।

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


3
यह ध्यान देने योग्य है कि विरासत में बहुरूपता को प्राप्त करने का एकमात्र तरीका नहीं है। डेकोरेटर पैटर्न रचना के माध्यम से बहुरूपता की उपस्थिति प्रदान करता है।
BitMask777

1
@ BitMask777: उप-प्रकार बहुरूपता केवल एक प्रकार का बहुरूपता है, दूसरा पैरामीट्रिक बहुरूपता होगा, इसके लिए आपको विरासत की आवश्यकता नहीं है। इसके अलावा अधिक महत्वपूर्ण बात: जब वंशानुक्रम के बारे में बात करते हैं, तो एक का अर्थ है वर्ग विरासत; .ie आप कई वर्गों के लिए एक सामान्य इंटरफ़ेस होने से उप-प्रकार के बहुरूपता हो सकते हैं, और आपको विरासत की समस्याएं नहीं मिलती हैं।
एग्गा

2
@engaga: मैंने आपकी टिप्पणी Inheritance is sometimes useful... That way you can have polymorphismको विरासत और बहुरूपता की अवधारणा को कड़ी से कड़ी के रूप में व्याख्या की (संदर्भ दिया गया उपप्रकार ग्रहण किया)। मेरी टिप्पणी का उद्देश्य यह बताना था कि आप अपनी टिप्पणी में क्या स्पष्ट करते हैं: कि विरासत में बहुरूपता को लागू करने का एकमात्र तरीका नहीं है, और वास्तव में रचना और विरासत के बीच निर्णय लेते समय निश्चित रूप से निर्धारण कारक नहीं है।
BitMask777

15

मान लीजिए कि एक विमान में केवल दो भाग होते हैं: एक इंजन और पंख।
फिर एयरक्राफ्ट क्लास डिजाइन करने के दो तरीके हैं।

Class Aircraft extends Engine{
  var wings;
}

अब आपका विमान स्थिर पंखों के साथ शुरू हो सकता है
और उन्हें मक्खी पर रोटरी पंखों में बदल सकता है। यह अनिवार्य रूप
से पंखों वाला इंजन है। लेकिन क्या होगा अगर मैं
मक्खी पर भी इंजन बदलना चाहता हूं ?

या तो आधार वर्ग Engineअपने
गुणों को बदलने के लिए एक उत्परिवर्ती को उजागर करता है , या मैं इसे फिर से डिज़ाइन Aircraftकरता हूं :

Class Aircraft {
  var wings;
  var engine;
}

अब, मैं अपने इंजन को फ्लाई पर भी बदल सकता हूं।


आपकी पोस्ट एक ऐसे बिंदु को सामने लाती है जिसे मैंने पहले नहीं माना था - कई हिस्सों के साथ यांत्रिक वस्तुओं की अपनी सादृश्यता को जारी रखने के लिए, एक बन्दूक की तरह कुछ पर, आमतौर पर एक भाग एक सीरियल नंबर के साथ चिह्नित होता है, जिसका सीरियल नंबर माना जाता है कि बन्दूक के एक पूरे के रूप में (एक handgun के लिए, यह आमतौर पर फ्रेम होगा)। कोई अन्य सभी भागों को बदल सकता है और फिर भी एक ही बन्दूक हो सकती है, लेकिन अगर फ्रेम में दरार पड़ती है और उसे बदलना पड़ता है, तो मूल बंदूक से अन्य सभी भागों के साथ एक नए फ्रेम को इकट्ठा करने का परिणाम एक नई बंदूक होगी। ध्यान दें कि ...
सुपरकैट

... तथ्य यह है कि एक बंदूक के कई हिस्सों में उन पर चिह्नित सीरियल नंबर हो सकते हैं, इसका मतलब यह नहीं है कि बंदूक में कई पहचान हो सकती हैं। फ्रेम पर केवल सीरियल नंबर बंदूक की पहचान करता है; किसी अन्य भाग पर क्रम संख्या यह पहचानती है कि उन भागों को किस बंदूक से निर्मित किया गया था, जिसे इकट्ठा किया जा सकता है, वह बंदूक नहीं हो सकती है जिस पर वे किसी भी समय इकट्ठे होते हैं।
सुपरकैट


7

जब आप बेस क्लास के एपीआई को "कॉपी" / एक्सपोज करना चाहते हैं, तो आप इनहेरिटेंस का उपयोग करते हैं। जब आप केवल कार्यक्षमता को "कॉपी" करना चाहते हैं, तो प्रतिनिधिमंडल का उपयोग करें।

इसका एक उदाहरण: आप एक सूची से एक स्टैक बनाना चाहते हैं। स्टैक में केवल पॉप, पुश और झांकना होता है। आपको दिए गए उत्तराधिकार का उपयोग नहीं करना चाहिए कि आप एक स्टैक में push_back, push_front, removeAt, et al. तरह की कार्यक्षमता नहीं चाहते हैं।


7

ये दो तरीके बस एक साथ ठीक रह सकते हैं और वास्तव में एक दूसरे का समर्थन करते हैं।

रचना बस इसे मॉड्यूलर खेल रही है: आप मूल वर्ग के समान इंटरफ़ेस बनाते हैं, नई ऑब्जेक्ट बनाते हैं और इसे कॉल करते हैं। यदि इन वस्तुओं को एक दूसरे के बारे में जानने की आवश्यकता नहीं है, तो यह संरचना का उपयोग करने के लिए काफी सुरक्षित और आसान है। यहाँ बहुत सारे कब्ज़े हैं।

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

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

क्यों?

क्योंकि विरासत जानकारी स्थानांतरित करने का एक खराब तरीका है

यहां रचना का एक वास्तविक किनारा है: रिश्ते को उलटा किया जा सकता है: "माता-पिता वर्ग" या "अमूर्त कार्यकर्ता" किसी भी विशिष्ट "बच्चे" वस्तुओं को कुछ इंटरफ़ेस को लागू कर सकते हैं + किसी भी बच्चे को किसी अन्य प्रकार के माता-पिता के अंदर सेट किया जा सकता है, जो स्वीकार करता है यह प्रकार है । और किसी भी संख्या में वस्तुएं हो सकती हैं, उदाहरण के लिए मर्जसॉर्ट या क्विकॉर्ट एक अमूर्त तुलना -डंडफेस लागू करने वाली वस्तुओं की किसी भी सूची को सॉर्ट कर सकता है। या इसे दूसरे तरीके से रखने के लिए: वस्तुओं का कोई समूह जो "फू ()" और वस्तुओं के अन्य समूह को लागू करता है जो "फू ()" वाली वस्तुओं का उपयोग एक साथ कर सकते हैं।

मैं वंशानुक्रम का उपयोग करने के तीन वास्तविक कारणों के बारे में सोच सकता हूं:

  1. आपके पास एक ही इंटरफ़ेस के साथ कई कक्षाएं हैं और आप उन्हें लिखने में समय बचाना चाहते हैं
  2. आपको प्रत्येक ऑब्जेक्ट के लिए एक ही बेस क्लास का उपयोग करना होगा
  3. आपको निजी चर को संशोधित करने की आवश्यकता है, जो किसी भी मामले में सार्वजनिक नहीं हो सकता है

यदि ये सच हैं, तो संभवतः विरासत का उपयोग करना आवश्यक है।

कारण 1 का उपयोग करने में कुछ भी बुरा नहीं है, आपकी वस्तुओं पर एक ठोस इंटरफ़ेस होना बहुत अच्छी बात है। यह रचना का उपयोग करके या विरासत के साथ किया जा सकता है, कोई समस्या नहीं - अगर यह इंटरफ़ेस सरल है और नहीं बदलता है। आमतौर पर वंशानुक्रम यहां काफी प्रभावी है।

यदि कारण संख्या 2 है तो यह थोड़ा मुश्किल हो जाता है। क्या आपको वास्तव में केवल उसी आधार वर्ग का उपयोग करने की आवश्यकता है? सामान्य तौर पर, बस एक ही आधार वर्ग का उपयोग करना पर्याप्त नहीं है, लेकिन यह आपके ढांचे की आवश्यकता हो सकती है, एक डिजाइन विचार जिसे टाला नहीं जा सकता है।

हालाँकि, यदि आप निजी चर, केस 3 का उपयोग करना चाहते हैं, तो आप मुश्किल में पड़ सकते हैं। यदि आप वैश्विक चर को असुरक्षित मानते हैं, तो आपको विरासत का उपयोग करने पर विचार करना चाहिए ताकि निजी चर भी असुरक्षित हो । ध्यान रखें, वैश्विक चर सभी बुरे नहीं होते हैं - डेटाबेस अनिवार्य रूप से वैश्विक चर का बड़ा समूह होते हैं। लेकिन अगर आप इसे संभाल सकते हैं, तो यह काफी ठीक है।


7

नए प्रोग्रामर्स के लिए इस प्रश्न को एक अलग दृष्टिकोण से संबोधित करने के लिए:

जब हम ऑब्जेक्ट-ओरिएंटेड प्रोग्रामिंग सीखते हैं तो इनहेरिटेंस अक्सर जल्दी सिखाई जाती है, इसलिए इसे एक सामान्य समस्या के आसान समाधान के रूप में देखा जाता है।

मेरे पास तीन वर्ग हैं जो सभी को कुछ सामान्य कार्यक्षमता की आवश्यकता है। इसलिए यदि मैं एक बेस क्लास लिखता हूं और उन सभी को इसमें से विरासत में मिला है, तो उन सभी में यह कार्यक्षमता होगी और मुझे केवल एक बार इसे बनाए रखने की आवश्यकता होगी।

यह बहुत अच्छा लगता है, लेकिन व्यवहार में यह लगभग कभी नहीं, कभी भी, कई कारणों में से एक काम करता है:

  • हमें पता चलता है कि कुछ अन्य कार्य हैं जो हम चाहते हैं कि हमारी कक्षाएं हों। यदि जिस तरह से हम कक्षाओं में कार्यक्षमता जोड़ते हैं वह वंशानुक्रम के माध्यम से है, तो हमें यह तय करना होगा - क्या हम इसे मौजूदा आधार वर्ग में जोड़ते हैं, भले ही हर वर्ग जो इसे विरासत में लेता है, उस कार्यक्षमता की आवश्यकता नहीं है? क्या हम एक और आधार वर्ग बनाते हैं? लेकिन उन वर्गों के बारे में क्या है जो पहले से ही दूसरे आधार वर्ग से विरासत में मिले हैं?
  • हमें पता चलता है कि हमारे बेस क्लास से विरासत में मिली सिर्फ एक कक्षा के लिए हम चाहते हैं कि बेस क्लास थोड़ा अलग व्यवहार करे। तो अब हम अपने बेस क्लास के साथ वापस जाते हैं और टिंकर करते हैं, हो सकता है कि कुछ वर्चुअल तरीके, या इससे भी बदतर, कुछ कोड जोड़ते हैं, जो कहते हैं, "अगर मुझे विरासत में टाइप A मिला है, तो ऐसा करें, लेकिन अगर मुझे विरासत में टाइप B मिला है, तो ऐसा करें । " यह बहुत कारणों से बुरा है। एक यह है कि हर बार जब हम आधार वर्ग को बदलते हैं, तो हम प्रभावी रूप से प्रत्येक विरासत वाले वर्ग को बदल रहे हैं। इसलिए हम वास्तव में वर्ग ए, बी, सी और डी बदल रहे हैं क्योंकि हमें कक्षा ए में थोड़ा अलग व्यवहार की आवश्यकता है। जैसा कि हम सोचते हैं कि हम सावधान हैं, हम उन कारणों में से एक वर्ग को तोड़ सकते हैं जिनका उन लोगों से कोई लेना-देना नहीं है। कक्षाएं।
  • हम जान सकते हैं कि हमने इन सभी वर्गों को एक-दूसरे से विरासत में बनाने का फैसला क्यों किया, लेकिन यह शायद (किसी को नहीं) किसी और को समझाना होगा, जिसे हमारे कोड को बनाए रखना होगा। हम उन्हें एक मुश्किल विकल्प के रूप में मजबूर कर सकते हैं - क्या मुझे वास्तव में बदसूरत और गड़बड़ करने के लिए कुछ करना है जो मुझे ज़रूरत है (पिछली बुलेट बिंदु देखें) या क्या मैं बस इस का एक गुच्छा फिर से लिखता हूं।

अंत में, हम अपने कोड को कुछ कठिन समुद्री मील में बाँधते हैं और इससे कोई लाभ नहीं मिलता है, सिवाय इसके कि हमें यह कहने के लिए मिलता है, "कूल, मुझे विरासत के बारे में पता चला और अब मैंने इसका इस्तेमाल किया।" इसका मतलब यह नहीं है कि कृपालु होने के कारण हमने यह सब किया है। लेकिन हम सबने ऐसा किया क्योंकि किसी ने हमें नहीं बताया।

जैसे ही किसी ने मुझे "वंशानुक्रम पर अनुकूल रचना" के बारे में समझाया, मैंने सोचा कि हर बार जब मैंने विरासत का उपयोग करके कक्षाओं के बीच कार्यक्षमता साझा करने का प्रयास किया और महसूस किया कि अधिकांश समय यह वास्तव में अच्छी तरह से काम नहीं करता था।

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

ओवरसाइप्लाइज़िंग के जोखिम पर, वह रचना है - एक साथ काम करने के लिए कई वर्गों की रचना। और एक बार जब हम उस आदत को बनाते हैं तो हम पाते हैं कि यह विरासत के उपयोग की तुलना में बहुत अधिक लचीला, बनाए रखने योग्य और परीक्षण योग्य है।


कई वर्ग एक से अधिक बेस क्लास का उपयोग नहीं कर सकते, इनहेरिटेंस पर एक खराब प्रतिबिंब नहीं है, बल्कि किसी विशेष भाषा की क्षमता की कमी पर खराब प्रतिबिंब है।
iPherian

इस उत्तर को लिखने के बाद के समय में, मैंने इस पोस्ट को "अंकल बॉब" से पढ़ा, जिसमें पता चला कि क्षमता की कमी है। मैंने कभी भी ऐसी भाषा का उपयोग नहीं किया है जो एकाधिक वंशानुक्रम की अनुमति देती है। लेकिन पीछे मुड़कर, सवाल "भाषा अज्ञेय" को टैग किया गया है और मेरा उत्तर C # मानता है। मुझे अपने क्षितिज को व्यापक बनाने की आवश्यकता है।
स्कॉट हैनन

6

एक तरफ से एक / एक विचार है, किसी को भी अपनी वस्तु के माध्यम से जाने की विरासत की "गहराई" पर विचार करना चाहिए। वंशानुक्रम के पाँच या छः स्तरों से परे कुछ भी अनपेक्षित कास्टिंग और बॉक्सिंग / अनबॉक्सिंग समस्याओं का कारण हो सकता है, और उन मामलों में आपकी वस्तु की रचना करना बुद्धिमानी हो सकती है।


6

जब आपके पास एक -ए है दो वर्गों के बीच संबंध होता है (उदाहरण के लिए कुत्ता एक कुत्ते है), तो आप विरासत के लिए जाते हैं।

दूसरी ओर जब आपके पास दो कक्षाओं (छात्र के पाठ्यक्रम) या (शिक्षक अध्ययन पाठ्यक्रम) के बीच एक या कुछ विशेषण संबंध होते हैं, तो आपने रचना को चुना।


आपने कहा विरासत और विरासत। क्या आपको विरासत और रचना से मतलब नहीं है?
trevorKirkby

नहीं, तुम नहीं। आप बस एक कैनाइन इंटरफ़ेस को परिभाषित कर सकते हैं और प्रत्येक कुत्ते को इसे लागू कर सकते हैं और आप अधिक ठोस कोड के साथ समाप्त करेंगे।
अंक

5

इसका एक सरल तरीका यह होगा कि वंशानुक्रम का उपयोग तब किया जाना चाहिए जब आपको अपनी कक्षा की एक वस्तु की आवश्यकता हो, जिसमें उसके मूल वर्ग के समान इंटरफ़ेस हो , ताकि उसे मूल वर्ग की वस्तु के रूप में माना जा सके (उत्थान) । इसके अलावा, एक व्युत्पन्न वर्ग वस्तु पर फ़ंक्शन कॉल कोड में हर जगह समान रहेगा, लेकिन कॉल करने की विशिष्ट विधि रनटाइम पर निर्धारित की जाएगी (यानी निम्न-स्तरीय कार्यान्वयन अलग - अलग, उच्च-स्तरीय इंटरफ़ेस समान रहता है)।

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


इंटरफ़ेस के उल्लेख के लिए +1। मैं मौजूदा कक्षाओं को छिपाने के लिए और रचना के लिए उपयोग की जाने वाली वस्तु का मजाक उड़ाकर अपनी नई कक्षा को उचित रूप से इकाई बनाने के लिए इस दृष्टिकोण का उपयोग करता हूं। इसके बजाय नए ऑब्जेक्ट के स्वामी को उम्मीदवार पैरेंट क्लास पास करने की आवश्यकता होती है।
केल


4

मैं @ फावले से सहमत हूं, जब वह कहता है, रचना के लिए जगह हैं और विरासत के लिए जगह हैं।

मुझे लगता है कि विरासत का उपयोग किया जाना चाहिए, यदि आपका उत्तर इनमें से किसी भी प्रश्न के लिए सकारात्मक है।

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

हालांकि, यदि आपका इरादा विशुद्ध रूप से कोड री-यूज का है, तो रचना की संभावना सबसे बेहतर डिजाइन विकल्प है।


4

कोड के पुन: उपयोग के लिए वंशानुक्रम एक बहुत शक्तिशाली machanism है। लेकिन इसका सही इस्तेमाल करने की जरूरत है। मैं कहूंगा कि यदि उपवर्ग मूल वर्ग का एक उपप्रकार है तो विरासत का सही ढंग से उपयोग किया जाता है। जैसा कि ऊपर उल्लेख किया गया है, Liskov प्रतिस्थापन सिद्धांत यहां प्रमुख बिंदु है।

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

जब हम कहते हैं कि संख्या 5 टाइप पूर्णांक की है, तो हम कह रहे हैं कि 5 संभावित मानों के एक समूह से संबंधित है (उदाहरण के लिए, जावा आदिम प्रकारों के लिए संभावित मान देखें)। हम यह भी कह रहे हैं कि मेरे पास अतिरिक्त और घटाव जैसे मूल्य पर प्रदर्शन करने के तरीकों का एक वैध सेट है। और अंत में हम यह कह रहे हैं कि गुणों का एक समूह है जो हमेशा संतुष्ट रहता है, उदाहरण के लिए, यदि मैं 3 और 5 मान जोड़ता हूं, तो मुझे परिणाम के रूप में 8 मिलेंगे।

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

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

class List {
  data = new Array();

  Integer size() {
    return data.length;
  }

  add(Integer anInteger) {
    data[data.length] = anInteger;
  }
}

उसके बाद, मैं पूर्णांकों की सूची के रूप में पूर्णांक का सेट लिखता हूं:

class Set, inheriting from: List {
  add(Integer anInteger) {
     if (data.notContains(anInteger)) {
       super.add(anInteger);
     }
  }
}

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

यह वंशानुक्रम के अनुचित उपयोग का एक उत्कृष्ट उदाहरण है। इस मामले में रचना का उपयोग करें।

(एक टुकड़ा: से विरासत का उपयोग ठीक से )।


3

अंगूठे का एक नियम जो मैंने सुना है कि विरासत का उपयोग तब किया जाना चाहिए जब इसका "ए-ए" संबंध और रचना जब इसका "ए-ए" हो। यहां तक ​​कि इसके साथ मुझे लगता है कि आपको हमेशा रचना की ओर झुकना चाहिए क्योंकि यह बहुत जटिलता को समाप्त करता है।


2

रचना v / s वंशानुक्रम एक विस्तृत विषय है। जो कुछ भी बेहतर है उसके लिए कोई वास्तविक उत्तर नहीं है क्योंकि मुझे लगता है कि यह सब सिस्टम के डिजाइन पर निर्भर करता है।

आम तौर पर वस्तु के बीच संबंध के प्रकार उनमें से किसी एक को चुनने के लिए बेहतर जानकारी प्रदान करते हैं।

यदि संबंध प्रकार "आईएस-ए" संबंध है तो वंशानुक्रम बेहतर दृष्टिकोण है। अन्यथा संबंध प्रकार "एचएएस-ए" संबंध है तो रचना बेहतर दृष्टिकोण करेगी।

यह पूरी तरह से इकाई संबंधों पर निर्भर करता है।


2

भले ही रचना पसंद किया जाता है, मैं के पेशेवरों को उजागर करना चाहते हैं वंशानुक्रम और के विपक्ष संरचना

वंशानुक्रम के नियम:

  1. यह एक तार्किक " आईएस ए" संबंध स्थापित करता है । यदि कार और ट्रक दो प्रकार के वाहन (बेस क्लास) हैं, तो चाइल्ड क्लास आईएस ए बेस क्लास है।

    अर्थात

    कार एक वाहन है

    ट्रक एक वाहन है

  2. विरासत के साथ, आप एक क्षमता को परिभाषित / संशोधित / विस्तारित कर सकते हैं

    1. बेस क्लास कोई कार्यान्वयन प्रदान नहीं करता है और उप-वर्ग को पूर्ण विधि (सार) को ओवरराइड करना पड़ता है => आप एक अनुबंध को लागू कर सकते हैं
    2. बेस क्लास डिफ़ॉल्ट कार्यान्वयन प्रदान करता है और उप-वर्ग व्यवहार को बदल सकता है => आप अनुबंध को फिर से परिभाषित कर सकते हैं
    3. उप-वर्ग पहले कथन के रूप में सुपर.methodName () कॉल करके बेस क्लास कार्यान्वयन में विस्तार जोड़ता है = आप एक अनुबंध का विस्तार कर सकते हैं
    4. बेस क्लास एल्गोरिथ्म की संरचना को परिभाषित करता है और उप-वर्ग एल्गोरिथ्म के एक हिस्से को ओवरराइड करेगा => आप बेस क्लास कंकाल में बदलाव के बिना Template_method को लागू कर सकते हैं

रचना की विधि:

  1. विरासत में, उपवर्ग सीधे आधार वर्ग विधि का आह्वान कर सकता है, भले ही यह आईएस के संबंध के कारण आधार वर्ग विधि को लागू नहीं कर रहा हो । यदि आप संरचना का उपयोग करते हैं, तो आपको निहित क्लास एपीआई को उजागर करने के लिए कंटेनर क्लास में विधियों को जोड़ना होगा

उदा। यदि कार में वाहन शामिल है और यदि आपको कार की कीमत प्राप्त करनी है , जिसे वाहन में परिभाषित किया गया है , तो आपका कोड इस तरह होगा

class Vehicle{
     protected double getPrice(){
          // return price
     }
} 

class Car{
     Vehicle vehicle;
     protected double getPrice(){
          return vehicle.getPrice();
     }
} 

मुझे लगता है कि यह सवाल का जवाब नहीं देता है
अलमेनग्रे

आप ओपी प्रश्न पर फिर से विचार कर सकते हैं। मैंने संबोधित किया है: प्रत्येक दृष्टिकोण के लिए क्या व्यापार-अप हैं?
रविन्द्र बाबू

जैसा कि आपने उल्लेख किया है, आप केवल "इनहेरिटेंस के पेशेवरों और रचना के विपक्ष" के बारे में बात करते हैं, न कि
ईएएस

पेशेवरों और विपक्ष व्यापार बंद प्रदान करता है क्योंकि वंशानुक्रम का संघटन रचना का विपक्ष है और रचना का विपक्ष वंशानुक्रम का पक्ष है।
रवींद्र बाबू

1

जैसा कि कई लोगों ने बताया, मैं सबसे पहले चेक से शुरू करूंगा - चाहे "ए-ए" संबंध मौजूद हो। यदि यह मौजूद है तो मैं आमतौर पर निम्नलिखित की जांच करता हूं:

क्या आधार वर्ग को त्वरित किया जा सकता है। यही है, क्या आधार वर्ग गैर-सार हो सकता है। यदि यह गैर-सार हो सकता है तो मैं आमतौर पर रचना पसंद करता हूं

उदाहरण 1. लेखाकार एक कर्मचारी है। लेकिन मैं वंशानुक्रम का उपयोग नहीं करूंगा क्योंकि एक कर्मचारी वस्तु को तत्काल किया जा सकता है।

उदा 2. पुस्तक एक सेलिंग इट है। एक SellItem त्वरित नहीं किया जा सकता है - यह अमूर्त अवधारणा है। इसलिए मैं विरासत का उपयोग करूंगा। सेलिंग इटेम एक सार आधार वर्ग है (या C # में इंटरफ़ेस )

आप इस दृष्टिकोण के बारे में क्या सोचते हैं?

इसके अलावा, मैं @anon उत्तर का समर्थन करता हूं कि वंशानुक्रम का उपयोग क्यों करें?

वंशानुक्रम का उपयोग करने का मुख्य कारण रचना का एक रूप नहीं है - ऐसा इसलिए है ताकि आप बहुरूपी व्यवहार प्राप्त कर सकें। यदि आपको बहुरूपता की आवश्यकता नहीं है, तो आपको शायद विरासत का उपयोग नहीं करना चाहिए।

@MatthieuM। /software/12439/code-smell-inheritance-abuse/12448#comment303759_12448 में कहते हैं

विरासत के साथ मुद्दा यह है कि इसका उपयोग दो ऑर्थोगोनल उद्देश्यों के लिए किया जा सकता है:

इंटरफ़ेस (बहुरूपता के लिए)

कार्यान्वयन (कोड पुन: उपयोग के लिए)

संदर्भ

  1. कौन सा वर्ग डिजाइन बेहतर है?
  2. वंशानुक्रम बनाम एकत्रीकरण

1
मुझे यकीन नहीं है कि 'बेस क्लास सार क्यों है?' चर्चा में आंकड़े .. एलएसपी: क्या सभी कार्य जो कुत्तों पर काम करते हैं यदि पूडल वस्तुओं को पारित किया जाता है? यदि हाँ, तो पूडल को कुत्ते के लिए प्रतिस्थापित किया जा सकता है और इसलिए यह कुत्ते से विरासत में मिल सकता है।
गिषु

@ गिशु धन्यवाद। मैं निश्चित रूप से एलएसपी में देखूंगा। लेकिन इससे पहले, क्या आप कृपया "एक उदाहरण प्रदान कर सकते हैं जहां विरासत उचित है जिसमें आधार वर्ग सार नहीं हो सकता है"। मुझे क्या लगता है, विरासत केवल तभी लागू होती है जब आधार वर्ग अमूर्त होता है। यदि बेस क्लास को अलग से तत्काल करने की आवश्यकता है, तो विरासत के लिए मत जाओ। यही है, भले ही एकाउंटेंट एक कर्मचारी है, विरासत का उपयोग न करें।
LCJ

1
WCF हाल ही में पढ़ रहा है। .Net फ्रेमवर्क में एक उदाहरण है सिंक्रोनाइज़ेशनकोटेक्स्ट (आधार + को तत्काल किया जा सकता है) जो एक थ्रेडपूल थ्रेड पर काम करते हैं। व्युत्पत्तियों में WinFormsSyncContext (UI थ्रेड पर कतार) और DispatcherSyncContext (WPF डिस्पैचर पर कतार)
Gishu

@ गिशु धन्यवाद हालांकि, यह अधिक उपयोगी होगा यदि आप बैंक डोमेन, एचआर डोमेन, रिटेल डोमेन या किसी अन्य लोकप्रिय डोमेन के आधार पर परिदृश्य प्रदान कर सकते हैं।
LCJ

1
माफ़ करना। मैं उन डोमेन से परिचित नहीं हूँ .. एक और उदाहरण अगर पिछले वाले को बहुत परेशान किया गया था, तो वह Winforms / WPF में कंट्रोल क्लास है। आधार / सामान्य नियंत्रण को तत्काल किया जा सकता है। व्युत्पत्तियों में लिस्टबॉक्स, टेक्स्टबॉक्स आदि शामिल हैं। अब जब मुझे लगता है कि, डेकोरेटर डिज़ाइन पैटर्न एक अच्छा उदाहरण है IMHO और उपयोगी भी। डेकोरेटर गैर-अमूर्त वस्तु से प्राप्त होता है जिसे वह लपेटना / सजाना चाहता है।
गिशु

1

मुझे लगता है कि किसी ने भी हीरे की समस्या का उल्लेख नहीं किया है , जो विरासत के साथ उत्पन्न हो सकता है।

एक नज़र में, यदि कक्षाएं B और C, A और दोनों को ओवरराइड करने की विधि X, और चतुर्थ श्रेणी D, B और C दोनों से विरासत में मिली हैं, और X को ओवरराइड नहीं करती हैं, तो XD के किस कार्यान्वयन का उपयोग करना चाहिए?

विकिपीडिया इस प्रश्न में चर्चा किए जा रहे विषय का एक अच्छा अवलोकन प्रदान करता है।


1
डी inherits बी और सी नहीं ए यदि हां, तो यह एक्स के कार्यान्वयन वर्ग ए में है कि प्रयोग करेंगे
फ़ेब्रिकियो

1
@fabricio: धन्यवाद, मैंने पाठ संपादित किया। वैसे, ऐसा परिदृश्य भाषाओं में नहीं हो सकता है जो कई वर्ग की विरासत की अनुमति नहीं देते हैं, क्या मैं सही हूं?
Veverke

हाँ आप सही हैं .. और मैंने कभी एक के साथ काम नहीं किया है, जो कई विरासत (हीरे की समस्या में उदाहरण के अनुसार) की अनुमति देता है ..
फैबियो
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.