language agnostic - विरासत पर संरचना पसंद करते हैं?




language-agnostic oop (20)

"विरासत पर संरचना को पसंद करें" एक डिजाइन सिद्धांत है, जो कहता है कि फिट होने पर विरासत का दुरुपयोग न करें।

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

जैसे कार में एक व्हील, बॉडी, इंजन इत्यादि हैं, लेकिन यदि आप यहां विरासत में हैं, तो यह कार एक व्हील है - जो गलत है।

आगे के स्पष्टीकरण के लिए, विरासत पर प्राथमिकता को संदर्भित करें

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


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

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

सूर्य के टिम बोदरेउ से एक और अधिक पूर्ण और ठोस उत्तर :

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

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

...

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

  • स्थिरांक को छोड़कर, कभी भी फ़ील्ड का पर्दाफाश न करें
  • तरीके या तो सार या अंतिम होगा
  • सुपरक्लास कन्स्ट्रक्टर से कोई विधि कॉल करें

...

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


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

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


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

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

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

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


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

Class Aircraft extends Engine{
  var wings;
}

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

या तो बेस क्लास Engine एक म्यूटेटर को बदलने के लिए खुलासा करता है
संपत्तियों, या मैं Aircraft रूप में फिर से डिजाइन:

Class Aircraft {
  var wings;
  var engine;
}

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


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

प्रक्रियात्मक संहिता

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

विरासत

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

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 Title;
   public Manager(Person p, Employee e)
   {
      this.Title = e.Title;
   }
}

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


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

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

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

यह एक अच्छा विचार की तरह दिखता है, लेकिन कई मामलों में यह कक्षा को एक ऑब्जेक्ट देता है जो foo () (या मैन्युअल रूप से foo () मैन्युअल रूप से प्रदान किया गया मान भी सेट करता है) कुछ बेस क्लास से नई कक्षा के उत्तराधिकारी के लिए निर्दिष्ट करने के लिए समारोह foo ()।

क्यूं कर?

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

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

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

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

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

कारण 1 का उपयोग करने में कुछ भी बुरा नहीं है, आपकी वस्तुओं पर ठोस इंटरफ़ेस रखना बहुत अच्छी बात है। This can be done using composition or with inheritance, no problem - if this interface is simple and does not change. Usually inheritance is quite effective here.

If the reason is number 2 it gets a bit tricky. Do you really only need to use the same base class? In general, just using the same base class is not good enough, but it may be a requirement of your framework, a design consideration which can not be avoided.

However, if you want to use the private variables, the case 3, then you may be in trouble. If you consider global variables unsafe, then you should consider using inheritance to get access to private variables also unsafe . Mind you, global variables are not all THAT bad - databases are essentially big set of global variables. But if you can handle it, then it's quite fine.


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

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

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

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


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


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


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

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

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

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

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


सभी मौजूदा उत्तरों यह बताने के लिए महान हैं कि क्यों, अक्सर, संरचना विरासत पर बेहतर है। हालांकि, वास्तव में ऐसी स्थितियां हैं जहां विरासत अधिक उपयुक्त है, इसलिए यह जानना महत्वपूर्ण है कि हम ऐसी स्थिति में हैं या नहीं, यह जानने के लिए एक अच्छा "परीक्षण" होना महत्वपूर्ण है।

मंडल-एलीप्स समस्या यह है कि विरासत का उपयोग करने के लिए " आइस -ए" वास्तव में एक अच्छा परीक्षण क्यों नहीं है, इसका एक महान उदाहरण है। दरअसल, हालांकि एक सर्कल एक अंडाकार है , Circle क्लास को Ellipse वर्ग का उत्तराधिकारी होना एक बुरा विचार है क्योंकि ऐसी चीजें हैं जो लंबवृत्त कर सकती हैं, लेकिन मंडल नहीं कर सकते हैं। उदाहरण के लिए, अंडाकार फैल सकता है, लेकिन मंडल नहीं कर सकते हैं। तो आप circle.stretch() हो सकते हैं, लेकिन circle.stretch()

मुझे लगता है कि एक बेहतर परीक्षण "है-ए" और "जैसी कार्यक्षमता-जैसी है" । दूसरे शब्दों में, जब भी subclass polymorphically इस्तेमाल किया जा सकता है विरासत का उपयोग करना एक अच्छा विचार है। "Is-a" संबंध polymorphic उपयोग के लिए एक आवश्यक शर्त है, लेकिन यह पर्याप्त नहीं है। यह भी बहुत महत्वपूर्ण है कि सुपरक्लास द्वारा प्रदान की गई हर कार्यक्षमता को उप-वर्ग द्वारा भी प्रदान किया जा सकता है। तो जब आपके पास एक वर्ग है जो "एक प्रकार का" है, लेकिन इसमें कुछ बाधाएं शामिल हैं, तो आपको विरासत का उपयोग नहीं करना चाहिए, क्योंकि इसे बहुरूप रूप से उपयोग नहीं किया जा सकता है।

दूसरे शब्दों में, विरासत गुणों को साझा करने के बारे में नहीं है, बल्कि कार्यक्षमता साझा करने के बारे में है । आम तौर पर, "is-a" परीक्षण के रूप में कार्य करता है कि क्या सुपरक्लास के सभी गेटर्स उप-वर्ग में समझ में आते हैं, जबकि "परीक्षण के रूप में बहुत अधिक कार्यक्षमता" एक परीक्षण के रूप में कार्य करता है कि क्या सुपरक्लास के सभी सेटर्स उप-वर्ग में समझते हैं । साथ में, गेटर्स और सेटर्स कक्षा के "कार्यक्षमता" को परिभाषित करते हैं। अकेले गेटर्स एक वर्ग के "गुण" को परिभाषित करते हैं। "सेटर" द्वारा, मेरा मतलब है कि कोई भी कार्य जो ऑब्जेक्ट की स्थिति को बदलता है, न कि केवल छोटे।

अन्य उत्तरों / टिप्पणियों ने काफी चीजें कहा, लेकिन मुझे लगता है कि मेरा कुछ स्पष्टता जोड़ता है।


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

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

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


A rule of thumb I have heard is inheritance should be used when its a "is-a" relationship and composition when its a "has-a". Even with that I feel that you should always lean towards composition because it eliminates a lot of complexity.


As many people told, I will first start with the check - whether there exists an "is-a" relationship. If it exists I usually check the following:

Whether the base class can be instantiated. That is, whether the base class can be non-abstract. If it can be non-abstract I usually prefer composition

Eg 1. Accountant is an Employee. But I will not use inheritance because a Employee object can be instantiated.

Eg 2. Book is a SellingItem. A SellingItem cannot be instantiated - it is abstract concept. Hence I will use inheritacne. The SellingItem is an abstract base class (or interface in C#)

What do you think about this approach?

Also, I support @anon answer in Why use inheritance at all?

The main reason for using inheritance is not as a form of composition - it is so you can get polymorphic behaviour. If you don't need polymorphism, you probably should not be using inheritance.

@MatthieuM. says in https://softwareengineering.stackexchange.com/questions/12439/code-smell-inheritance-abuse/12448#comment303759_12448

The issue with inheritance is that it can be used for two orthogonal purposes:

interface (for polymorphism)

implementation (for code reuse)

REFERENCE

  1. Which class design is better?
  2. Inheritance vs. Aggregation

Aside from is a/has a considerations, one must also consider the "depth" of inheritance your object has to go through. Anything beyond five or six levels of inheritance deep might cause unexpected casting and boxing/unboxing problems, and in those cases it might be wise to compose your object instead.


Even though Composition is preferred, I would like to highlight pros of Inheritance and cons of Composition .

Pros of Inheritance:

  1. It establishes a logical " IS A" relation. If Car and Truck are two types of Vehicle ( base class), child class IS A base class.

    अर्थात

    Car is a Vehicle

    Truck is a Vehicle

  2. With inheritance, you can define/modify/extend a capability

    1. Base class provides no implementation and sub-class has to override complete method (abstract) => You can implement a contract
    2. Base class provides default implementation and sub-class can change the behaviour => You can re-define contract
    3. Sub-class adds extension to base class implementation by calling super.methodName() as first statement => You can extend a contract
    4. Base class defines structure of the algorithm and sub-class will override a part of algorithm => You can implement Template_method without change in base class skeleton

Cons of Composition:

  1. In inheritance, subclass can directly invoke base class method even though it's not implementing base class method because of IS A relation. If you use composition, you have to add methods in container class to expose contained class API

eg If Car contains Vehicle and if you have to get price of the Car , which has been defined in Vehicle , your code will be like this

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

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

I agree with @Pavel, when he says, there are places for composition and there are places for inheritance.

I think inheritance should be used if your answer is an affirmative to any of these questions.

  • Is your class part of a structure that benefits from polymorphism ? For example, if you had a Shape class, which declares a method called draw(), then we clearly need Circle and Square classes to be subclasses of Shape, so that their client classes would depend on Shape and not on specific subclasses.
  • Does your class need to re-use any high level interactions defined in another class ? The template method design pattern would be impossible to implement without inheritance. I believe all extensible frameworks use this pattern.

However, if your intention is purely that of code re-use, then composition most likely is a better design choice.


Subtyping is appropriate and more powerful where the invariants can be enumerated , else use function composition for extensibility.


To address this question from a different perspective for newer programmers:

Inheritance is often taught early when we learn object-oriented programming, so it's seen as an easy solution to a common problem.

I have three classes that all need some common functionality. So if I write a base class and have them all inherit from it, then they will all have that functionality and I'll only need to maintain it in once place.

It sounds great, but in practice it almost never, ever works, for one of several reasons:

  • We discover that there are some other functions that we want our classes to have. If the way that we add functionality to classes is through inheritance, we have to decide - do we add it to the existing base class, even though not every class that inherits from it needs that functionality? Do we create another base class? But what about classes that already inherit from the other base class?
  • We discover that for just one of the classes that inherits from our base class we want the base class to behave a little differently. So now we go back and tinker with our base class, maybe adding some virtual methods, or even worse, some code that says, "If I'm inherited type A, do this, but if I'm inherited type B, do that." That's bad for lots of reasons. One is that every time we change the base class, we're effectively changing every inherited class. So we're really changing class A, B, C, and D because we need a slightly different behavior in class A. As careful as we think we are, we might break one of those classes for reasons that have nothing to do with those classes.
  • We might know why we decided to make all of these classes inherit from each other, but it might not (probably won't) make sense to someone else who has to maintain our code. We might force them into a difficult choice - do I do something really ugly and messy to make the change I need (see the previous bullet point) or do I just rewrite a bunch of this.

In the end, we tie our code in some difficult knots and get no benefit whatsoever from it except that we get to say, "Cool, I learned about inheritance and now I used it." That's not meant to be condescending because we've all done it. But we all did it because no one told us not to.

As soon as someone explained "favor composition over inheritance" to me, I thought back over every time I tried to share functionality between classes using inheritance and realized that most of the time it didn't really work well.

The antidote is the Single Responsibility Principle . Think of it as a constraint. My class must do one thing. I must be able to give my class a name that somehow describes that one thing it does. (There are exceptions to everything, but absolute rules are sometimes better when we're learning.) It follows that I cannot write a base class called ObjectBaseThatContainsVariousFunctionsNeededByDifferentClasses . Whatever distinct functionality I need must be in its own class, and then other classes that need that functionality can depend on that class, not inherit from it.

At the risk of oversimplifying, that's composition - composing multiple classes to work together. And once we form that habit we find that it's much more flexible, maintainable, and testable than using inheritance.







aggregation