वंशानुक्रम पर रचना क्यों पसंद करते हैं? प्रत्येक दृष्टिकोण के लिए क्या व्यापार-नापसंद हैं? आपको रचना पर विरासत का चयन कब करना चाहिए?
वंशानुक्रम पर रचना क्यों पसंद करते हैं? प्रत्येक दृष्टिकोण के लिए क्या व्यापार-नापसंद हैं? आपको रचना पर विरासत का चयन कब करना चाहिए?
जवाबों:
इनहेरिटेंस पर रचना को प्राथमिकता दें क्योंकि यह बाद में संशोधित करने के लिए अधिक निंदनीय / आसान है, लेकिन हमेशा कंपोज-एप्रोच का उपयोग न करें। रचना के साथ, डिपेंडेंसी इंजेक्शन / सेटर्स के साथ मक्खी पर व्यवहार को बदलना आसान है। विरासत अधिक कठोर है क्योंकि अधिकांश भाषाएं आपको एक से अधिक प्रकार से प्राप्त करने की अनुमति नहीं देती हैं। एक बार जब आप टाइपा से निकलते हैं, तो हंस कम या ज्यादा पकाया जाता है।
उपरोक्त के लिए मेरा एसिड परीक्षण है:
क्या TypeB पूर्ण प्रकार के इंटरफ़ेस (सभी सार्वजनिक विधियों से कम नहीं) को टाइप करना चाहता है, जिससे TypeB का उपयोग किया जा सकता है जहाँ TypeA अपेक्षित है? वंशानुक्रम का संकेत देता है ।
क्या TypeB केवल TypeA द्वारा उजागर किए गए व्यवहार का कुछ / हिस्सा चाहता है? संकेत के लिए रचना की आवश्यकता है ।
अद्यतन: बस मेरे जवाब पर वापस आया और अब ऐसा लगता है कि यह बारबरा लिस्कॉव के लिस्कोव प्रतिस्थापन सिद्धांत के एक विशिष्ट उल्लेख के बिना अधूरा है 'क्या मुझे इस प्रकार से विरासत में मिलना चाहिए?'
एक के रूप में होने के बारे में सोचो एक है संबंध के संबंध के । एक कार "में एक" इंजन, एक व्यक्ति "का एक" नाम आदि होता है।
एक रिश्ते के रूप में विरासत के बारे में सोचो । एक कार "एक" वाहन, एक व्यक्ति "एक" स्तनपायी, आदि है।
मैं इस दृष्टिकोण का कोई श्रेय नहीं लेता। मैंने इसे सीधे स्टीव मैककोनेल , धारा 6.3 द्वारा कोड के दूसरे संस्करण से लिया ।
यदि आप अंतर को समझते हैं, तो समझाना आसान है।
इसका एक उदाहरण कक्षाओं के उपयोग के बिना 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;
}
}
प्रबंधक वस्तु को एक कर्मचारी और एक व्यक्ति के रूप में बनाया गया है। शीर्षक व्यवहार कर्मचारी से लिया जाता है। यह स्पष्ट रचना अन्य चीजों के बीच अस्पष्टता को दूर करती है और आप कम बग का सामना करेंगे।
विरासत द्वारा प्रदान किए गए सभी निर्विवाद लाभों के साथ, यहां इसके कुछ नुकसान हैं।
वंशानुक्रम के नुकसान:
दूसरी ओर ऑब्जेक्ट रचना को अन्य वस्तुओं के संदर्भ के माध्यम से रनटाइम पर परिभाषित किया जाता है। ऐसे मामले में ये ऑब्जेक्ट कभी भी एक-दूसरे के संरक्षित डेटा (कोई एनकैप्सुलेशन ब्रेक) तक नहीं पहुंच पाएंगे और एक-दूसरे के इंटरफेस का सम्मान करने के लिए मजबूर होंगे। और इस मामले में भी, विरासत निर्भरता के मामले में कार्यान्वयन निर्भरता बहुत कम होगी।
एक और, बहुत व्यावहारिक कारण, वंशानुक्रम पर रचना पसंद करने के लिए अपने डोमेन मॉडल के साथ क्या करना है, और इसे संबंधित डेटाबेस में मैप करना है। एसक्यूएल मॉडल के लिए विरासत को मैप करना वास्तव में कठिन है (आप हर तरह के हैकी वर्कअराउंड के साथ समाप्त होते हैं, जैसे कि हमेशा उपयोग नहीं किए जाने वाले कॉलम बनाना, विचारों का उपयोग करना, आदि)। कुछ ओआरएमएल इससे निपटने की कोशिश करते हैं, लेकिन यह हमेशा जल्दी जटिल हो जाता है। संरचना को दो तालिकाओं के बीच एक विदेशी-कुंजी संबंध के माध्यम से आसानी से तैयार किया जा सकता है, लेकिन विरासत बहुत कठिन है।
जबकि कम शब्दों में मैं "उत्तराधिकार पर रचना को प्राथमिकता देता हूं" से सहमत होता हूं, बहुत बार मेरे लिए ऐसा लगता है कि "कोका-कोला पर आलू पसंद करते हैं"। वंशानुक्रम के लिए स्थान हैं और रचना के लिए स्थान हैं। आपको अंतर समझने की आवश्यकता है, फिर यह प्रश्न गायब हो जाएगा। मेरे लिए वास्तव में इसका मतलब है "यदि आप विरासत का उपयोग करने जा रहे हैं - फिर से सोचें, संभावना है कि आपको रचना की आवश्यकता है"।
जब आप खाना चाहते हैं, तब आपको कोका कोला के ऊपर आलू को प्राथमिकता देनी चाहिए और जब आप पीना चाहें तो कोका कोला को आलू के ऊपर डाल दें।
सुपरक्लास विधियों को कॉल करने के लिए केवल एक सुविधाजनक तरीका से अधिक उपवर्ग बनाना चाहिए। जब सबक्लास है तो आपको इनहेरिटेंस का उपयोग करना चाहिए "एक" सुपर क्लास दोनों संरचनात्मक और कार्यात्मक रूप से, जब इसे सुपरक्लास के रूप में इस्तेमाल किया जा सकता है और आप इसका उपयोग करने जा रहे हैं। यदि यह मामला नहीं है - यह विरासत नहीं है, लेकिन कुछ और है। रचना तब होती है जब आपकी वस्तुओं में कोई दूसरा होता है, या उनसे कोई संबंध होता है।
इसलिए मेरे लिए ऐसा लगता है कि अगर किसी को नहीं पता कि उसे विरासत या रचना की जरूरत है, तो असली समस्या यह है कि वह नहीं जानता कि वह पीना चाहता है या खाना। अपने समस्या डोमेन के बारे में अधिक सोचें, इसे बेहतर समझें।
InternalCombustionEngine
एक व्युत्पन्न वर्ग के साथ एक आधार वर्ग पर विचार करें GasolineEngine
। उत्तरार्द्ध स्पार्क प्लग जैसी चीजों को जोड़ता है, जिसमें बेस क्लास का अभाव है, लेकिन इस चीज का उपयोग एक InternalCombustionEngine
स्पार्क प्लग के रूप में किया जाएगा।
वंशानुक्रम विशेष रूप से प्रक्रियात्मक-भूमि से आता है और यह अक्सर भ्रामक सुरुचिपूर्ण दिखता है। मेरा मतलब यह है कि मुझे यह करने की ज़रूरत है कि इस एक कार्यक्षमता को किसी अन्य वर्ग में जोड़ा जाए, है ना? खैर, समस्याओं में से एक है
संरक्षित सदस्यों के रूप में उपवर्गों को कार्यान्वयन विवरणों को उजागर करने से आपका बेस क्लास इनकैप्सुलेशन तोड़ता है। यह आपके सिस्टम को कठोर और नाजुक बनाता है। अधिक दुखद दोष यह है कि नया उपवर्ग अपने साथ विरासत की श्रृंखला के सभी सामान और राय लाता है।
लेख, इनहेरिटेंस इविल: द एपिक फेल ऑफ द डेटानोटेशनमॉडेलबाइंडर , सी # में इस के एक उदाहरण के माध्यम से चलता है। यह विरासत के उपयोग को दर्शाता है जब रचना का उपयोग किया जाना चाहिए था और इसे फिर से कैसे बनाया जा सकता था।
जावा या C # में, एक वस्तु तुरंत टाइप होने के बाद उसका प्रकार नहीं बदल सकती है।
इसलिए, यदि आपकी वस्तु को एक अलग वस्तु के रूप में प्रकट होने की आवश्यकता है या किसी वस्तु स्थिति या स्थितियों के आधार पर अलग तरह से व्यवहार करना है, तो संरचना का उपयोग करें : देखें राज्य और रणनीति का डिजाइन पैटर्न का ।
यदि ऑब्जेक्ट को एक ही प्रकार का होना चाहिए, तो इनहेरिटेंस का उपयोग करें या इंटरफेस को लागू करें।
Client
। फिर, PreferredClient
बाद में एक चबूतरे की एक नई अवधारणा । PreferredClient
वंशानुक्रम होना चाहिए Client
? एक पसंदीदा ग्राहक 'ग्राहक' है, नहीं? खैर, इतनी जल्दी नहीं ... जैसा कि आपने कहा कि ऑब्जेक्ट्स रनटाइम पर अपनी क्लास नहीं बदल सकते। आप client.makePreferred()
ऑपरेशन को कैसे मॉडल करेंगे ? शायद जवाब एक लापता अवधारणा के साथ रचना का उपयोग करने में निहित है, एक Account
शायद?
Client
कक्षाएं होने के बजाय , शायद वहाँ सिर्फ एक है जो एक की अवधारणा को Account
StandardAccount
PreferredAccount
यहाँ संतोषजनक उत्तर नहीं मिला, इसलिए मैंने एक नया लिखा।
यह समझने के लिए कि " वंशानुक्रम पर रचना को क्यों पसंद करते हैं ", हमें पहले इस संक्षिप्त मुहावरे में छोड़ी गई धारणा को वापस लाने की आवश्यकता है।
वंशानुक्रम के दो लाभ हैं: उपप्रकार और उपवर्ग
सबटाइपिंग का अर्थ है एक प्रकार (इंटरफ़ेस) हस्ताक्षर, यानी एपीआई का एक सेट के अनुरूप, और एक व्यक्ति बहुरूपता को प्राप्त करने के लिए हस्ताक्षर के भाग को ओवरराइड कर सकता है।
सबक्लासिंग का अर्थ है विधि के कार्यान्वयन का निहित पुन: उपयोग।
दो लाभों के साथ विरासत करने के लिए दो अलग-अलग उद्देश्य आते हैं: उन्मुखीकरण और कोड का पुन: उपयोग करना।
यदि कोड का पुन: उपयोग एकमात्र उद्देश्य है, तो उप-वर्ग एक से अधिक दे सकता है जो उसे चाहिए, अर्थात माता-पिता वर्ग के कुछ सार्वजनिक तरीके बच्चे की कक्षा के लिए बहुत मायने नहीं रखते हैं। इस मामले में, विरासत पर रचना का पक्ष लेने के बजाय, रचना की मांग की जाती है । यह भी है जहाँ "is-a" बनाम "has-a" धारणा आती है।
इसलिए केवल तब जब उप-विभाजन का उपयोग किया जाता है, यानी बाद में एक बहुरूपिए तरीके से नए वर्ग का उपयोग करने के लिए, क्या हम विरासत या रचना को चुनने की समस्या का सामना करते हैं। यह वह धारणा है जो चर्चा के तहत संक्षिप्त मुहावरे में छोड़ी जाती है।
उपप्रकार के लिए एक प्रकार के हस्ताक्षर के अनुरूप है, इसका मतलब यह है कि रचना हमेशा प्रकार के एपीआई की कम मात्रा को उजागर करने के लिए होती है। अब व्यापार बंद किक में:
वंशानुक्रम सीधे ओवरड्यूड कोड प्रदान करता है यदि ओवरराइड नहीं किया जाता है, जबकि रचना को हर एपीआई को फिर से कोड करना पड़ता है, भले ही यह प्रतिनिधिमंडल का एक सरल काम हो।
इनहेरिटेंस आंतरिक पॉलीमॉर्फिक साइट के माध्यम से सीधा खुला पुनरावृत्ति प्रदान करता है this
, अर्थात किसी अन्य सदस्य फ़ंक्शन में ओवरराइडिंग विधि (या यहां तक कि प्रकार ) को सार्वजनिक या निजी (हालांकि हतोत्साहित )। खुली पुनरावृत्ति को रचना के माध्यम से अनुकरण किया जा सकता है , लेकिन इसके लिए अतिरिक्त प्रयास की आवश्यकता होती है और हमेशा व्यवहार्य (?) नहीं हो सकती है। नकल वाले सवाल का यह जवाब कुछ इसी तरह की बात करता है।
वंशानुक्रम संरक्षित सदस्यों को उजागर करता है । इससे अभिभावक वर्ग का एनकैप्सुलेशन टूट जाता है, और यदि उपवर्ग द्वारा उपयोग किया जाता है, तो बच्चे और उसके माता-पिता के बीच एक और निर्भरता पेश की जाती है।
संरचना में नियंत्रण के उलटा होने की संभावना है, और इसकी निर्भरता को गतिशील रूप से इंजेक्ट किया जा सकता है, जैसा कि डेकोरेटर पैटर्न और प्रॉक्सी पैटर्न में दिखाया गया है ।
कॉम्बीनेटर-ओरिएंटेड प्रोग्रामिंग का लाभ कंपोजिट है , अर्थात समग्र पैटर्न की तरह काम करना ।
रचना तुरंत एक अंतरफलक के लिए प्रोग्रामिंग का अनुसरण करती है ।
रचना में आसान कई विरासत का लाभ है ।
मन में उपरोक्त व्यापार के उतार के साथ, हम इसलिए विरासत पर रचना पसंद करते हैं । फिर भी कसकर संबंधित वर्गों के लिए, अर्थात जब निहित कोड का पुन: उपयोग वास्तव में लाभ पहुंचाता है, या खुली पुनरावृत्ति की जादू शक्ति वांछित है, तो विरासत का विकल्प होगा।
व्यक्तिगत रूप से मैंने हमेशा विरासत पर रचना को प्राथमिकता देना सीखा। कोई प्रोग्रामेटिक समस्या नहीं है जिसे आप विरासत के साथ हल कर सकते हैं जिसे आप रचना के साथ हल नहीं कर सकते हैं; हालाँकि आपको कुछ मामलों में Interfaces (Java) या प्रोटोकॉल (Obj-C) का उपयोग करना पड़ सकता है। चूंकि C ++ ऐसी किसी भी चीज़ को नहीं जानता है, इसलिए आपको अमूर्त आधार वर्गों का उपयोग करना होगा, जिसका अर्थ है कि आप C ++ में विरासत से पूरी तरह छुटकारा नहीं पा सकते हैं।
रचना अक्सर अधिक तार्किक होती है, यह बेहतर अमूर्तता प्रदान करती है, बेहतर एनकैप्सुलेशन, बेहतर कोड का पुन: उपयोग (विशेषकर बहुत बड़ी परियोजनाओं में) और कुछ दूरी पर कुछ भी टूटने की संभावना कम होती है क्योंकि आपने अपने कोड में कहीं भी एक अलग परिवर्तन किया है। यह " सिंगल रिस्पॉन्सिबिलिटी प्रिंसिपल " को बनाए रखने में भी आसान बनाता है , जिसे अक्सर संक्षेप में " एक वर्ग को बदलने के लिए एक से अधिक कारण नहीं होना चाहिए। ", और इसका मतलब है कि प्रत्येक वर्ग एक विशिष्ट उद्देश्य के लिए मौजूद है और यह केवल ऐसे तरीके हैं जो सीधे इसके उद्देश्य से संबंधित हैं। इसके अलावा एक बहुत उथले विरासत पेड़ होने के कारण अवलोकन को बनाए रखना बहुत आसान हो जाता है जब आपकी परियोजना वास्तव में बड़ी होने लगती है। बहुत से लोग सोचते हैं कि विरासत हमारी वास्तविक दुनिया का प्रतिनिधित्व करती हैबहुत अच्छी तरह से, लेकिन यह सच नहीं है। वास्तविक दुनिया विरासत की तुलना में बहुत अधिक संरचना का उपयोग करती है। बहुत अधिक हर वास्तविक विश्व वस्तु जिसे आप अपने हाथ में पकड़ सकते हैं, अन्य छोटी दुनिया की वस्तुओं से बना है।
हालांकि रचना के डाउनसाइड्स हैं। यदि आप विरासत को पूरी तरह से छोड़ देते हैं और केवल रचना पर ध्यान केंद्रित करते हैं, तो आप देखेंगे कि आपको अक्सर अतिरिक्त कोड लाइनों के एक जोड़े को लिखना होगा जो कि यदि आपने विरासत का उपयोग किया था तो आवश्यक नहीं थे। आपको कभी-कभी खुद को दोहराने के लिए भी मजबूर किया जाता है और इसका उल्लंघन होता है DRY सिद्धांत(DRY = खुद को न दोहराएं)। इसके अलावा रचना को अक्सर प्रतिनिधिमंडल की आवश्यकता होती है, और एक विधि किसी अन्य ऑब्जेक्ट की एक अन्य विधि को बुला रही है, जिसमें इस कोड के आसपास कोई अन्य कोड नहीं है। इस तरह के "डबल मेथड कॉल्स" (जो आसानी से ट्रिपल या चौगुनी मेथड कॉल्स और उससे भी आगे तक बढ़ सकते हैं) में इनहेरिटेंस की तुलना में बहुत बुरा प्रदर्शन होता है, जहाँ आपको बस अपने माता-पिता की एक विधि विरासत में मिलती है। विरासत में मिली विधि को कॉल करना गैर-विरासत वाले को कॉल करने के समान तेज़ हो सकता है, या यह थोड़ा धीमा हो सकता है, लेकिन आमतौर पर दो लगातार विधि कॉलों की तुलना में तेजी से होता है।
आपने देखा होगा कि अधिकांश OO भाषाएँ एकाधिक वंशानुक्रम की अनुमति नहीं देती हैं। हालांकि ऐसे कुछ मामले हैं जहां कई विरासत वास्तव में आपको कुछ खरीद सकते हैं, लेकिन वे नियम के बजाय अपवाद हैं। जब भी आप ऐसी स्थिति में दौड़ते हैं, जहां आपको लगता है कि "कई विरासत इस समस्या को हल करने के लिए एक बहुत अच्छी सुविधा होगी", तो आप आमतौर पर एक ऐसे बिंदु पर होते हैं, जहां आपको विरासत के बारे में पूरी तरह से सोचना चाहिए, क्योंकि यहां तक कि इसके लिए अतिरिक्त कोड लाइनों की एक जोड़ी की आवश्यकता हो सकती है , रचना पर आधारित एक समाधान आमतौर पर अधिक सुरुचिपूर्ण, लचीला और भविष्य के प्रमाण के रूप में सामने आएगा।
वंशानुक्रम वास्तव में एक शांत विशेषता है, लेकिन मुझे डर है कि यह पिछले कुछ वर्षों में अति प्रयोग किया गया है। लोगों ने विरासत का इलाज किया एक हथौड़ा के रूप में जो यह सब कील कर सकता है, भले ही यह वास्तव में एक नाखून, एक पेंच या शायद पूरी तरह से अलग हो।
TextFile
है a File
।
अंगूठे का मेरा सामान्य नियम: विरासत का उपयोग करने से पहले, विचार करें कि क्या रचना अधिक समझ में आती है।
कारण: उपवर्ग का मतलब आमतौर पर अधिक जटिलता और जुड़ाव होता है, यानी गलतियाँ करने के बिना बदलना, बनाए रखना, और पैमाना।
टिम बोउद्रेउ ऑफ़ सन से एक बहुत अधिक पूर्ण और ठोस उत्तर :
मैं देख रहा हूँ के रूप में विरासत के उपयोग के लिए आम समस्याओं:
- निर्दोष कृत्यों के अनपेक्षित परिणाम हो सकते हैं - इसका उत्कृष्ट उदाहरण सुपरक्लास कंस्ट्रक्टर से अतिप्रयोग योग्य तरीकों के लिए कॉल है, इससे पहले कि उप-वर्ग उदाहरण क्षेत्रों को आरम्भ किया गया है। एक आदर्श दुनिया में, कोई भी ऐसा कभी नहीं करेगा। यह एक आदर्श दुनिया नहीं है।
- यह विधि कॉल के क्रम के बारे में धारणा बनाने के लिए उपवर्गों के लिए व्यापक प्रलोभन प्रदान करता है और इस तरह की - ऐसी धारणाएं स्थिर नहीं होती हैं यदि सुपरक्लास समय के साथ विकसित हो सकता है। मेरे टोस्टर और कॉफी पॉट सादृश्य भी देखें ।
- कक्षाएं भारी हो जाती हैं - आप जरूरी नहीं जानते हैं कि आपका सुपरक्लास अपने कंस्ट्रक्टर में क्या काम कर रहा है, या यह कितनी मेमोरी का उपयोग करने वाला है। तो कुछ निर्दोष हो सकता है-हो सकता है कि हल्के वस्तु आप की तुलना में कहीं अधिक महंगा हो सकता है, और यह समय के साथ बदल सकता है अगर सुपरस्टार घूमा करता है
- यह उपवर्गों के विस्फोट को प्रोत्साहित करता है । क्लास लोडिंग में समय लगता है, अधिक कक्षाओं में मेमोरी खर्च होती है। यह तब तक एक गैर-मुद्दा हो सकता है जब तक आप नेटबीन्स के पैमाने पर एक ऐप के साथ काम नहीं कर रहे हैं, लेकिन वहां, हमारे पास वास्तविक मुद्दे थे, उदाहरण के लिए, मेनू धीमा हो रहा है क्योंकि मेनू के पहले प्रदर्शन ने बड़े पैमाने पर क्लास लोडिंग चालू कर दिया। हमने इसे और अधिक घोषणात्मक सिंटैक्स और अन्य तकनीकों में स्थानांतरित करके तय किया, लेकिन उस लागत को भी ठीक करने के लिए।
- बाद में चीजों को बदलना कठिन हो जाता है - यदि आपने एक वर्ग को सार्वजनिक कर दिया है, तो सुपरक्लास को स्वैप करके उपवर्गों को तोड़ने जा रहा है - यह एक विकल्प है, जो एक बार कोड को सार्वजनिक करने के बाद, आपसे शादी कर लेता है। इसलिए यदि आप अपने सुपरक्लास में वास्तविक कार्यक्षमता में परिवर्तन नहीं कर रहे हैं, तो आपको बाद में यदि आप उपयोग करते हैं, तो अपनी ज़रूरत की चीज़ों को बढ़ाने के बजाय चीजों को बदलने की अधिक स्वतंत्रता मिलती है। उदाहरण के लिए, JPanel को उपवर्ग में लेना - यह आमतौर पर गलत है; और अगर उपवर्ग कहीं सार्वजनिक है, तो आपको कभी भी उस फैसले को दोबारा करने का मौका नहीं मिलता है। यदि इसे JComponent getThePanel () के रूप में एक्सेस किया जाता है, तो आप अभी भी इसे कर सकते हैं (संकेत: अपने एपीआई के रूप में घटकों के लिए मॉडल का पर्दाफाश करें)।
- ऑब्जेक्ट पदानुक्रम स्केल नहीं करते हैं (या बाद में उन्हें स्केल करना आगे की योजना बनाने की तुलना में बहुत कठिन है) - यह क्लासिक "बहुत सारी परतें" समस्या है। मैं इसके नीचे जाता हूँ, और AskTheOracle पैटर्न इसे कैसे हल कर सकता है (हालाँकि यह OOP शुद्धतावादियों को नाराज कर सकता है)।
...
मेरा क्या करना है, यदि आप विरासत के लिए अनुमति देते हैं, जो आप नमक के दाने के साथ ले सकते हैं:
- स्थिरांक को छोड़कर, कभी भी कोई भी क्षेत्र उजागर न करें
- विधियाँ या तो अमूर्त या अंतिम होंगी
- सुपरक्लास कंस्ट्रक्टर से कोई विधि न कहें
...
यह सब बड़े लोगों की तुलना में छोटी परियोजनाओं के लिए, और सार्वजनिक लोगों की तुलना में निजी कक्षाओं के लिए कम लागू होता है
अन्य उत्तर देखें।
यह अक्सर कहा जाता है कि एक वर्ग एक वर्ग Bar
को विरासत में दे सकता है Foo
जब निम्नलिखित वाक्य सत्य होता है:
- एक बार एक फू है
दुर्भाग्य से, उपरोक्त परीक्षण अकेले विश्वसनीय नहीं है। इसके बजाय निम्नलिखित का उपयोग करें:
- एक बार एक फू है, और
- बार वो सबकुछ कर सकता है जो फॉक्स कर सकता है।
पहला परीक्षण सुनिश्चित सब ही टिककर खेल की Foo
में कर भावना Bar
(= साझा गुण) है, जबकि दूसरे टेस्ट सुनिश्चित करती है कि सभी setters की Foo
में कर भावना Bar
(= साझा कार्यक्षमता)।
उदाहरण 1: कुत्ता -> पशु
कुत्ता एक जानवर है और कुत्ते वह सब कुछ कर सकते हैं जो जानवर कर सकते हैं (जैसे कि साँस लेना, मरना, आदि)। इसलिए, वर्ग वर्ग Dog
को विरासत में दे सकता हैAnimal
।
उदाहरण 2: वृत्त - / -> दीर्घवृत्त
एक वृत्त एक दीर्घवृत्त है, लेकिन वृत्त वह सब कुछ नहीं कर सकता जो दीर्घवृत्त कर सकता है। उदाहरण के लिए, वृत्त नहीं खींच सकते, जबकि दीर्घवृत्त हो सकते हैं। इसलिए, वर्ग वर्ग Circle
को इनहेरिट नहीं कर सकता हैEllipse
।
इसे सर्कल-एलिप्से समस्या कहा जाता है , जो वास्तव में एक समस्या नहीं है, बस एक स्पष्ट प्रमाण है कि अकेले पहला परीक्षण पर्याप्त नहीं है जो यह निष्कर्ष निकाल सके कि विरासत संभव है। विशेष रूप से, यह उदाहरण हाइलाइट किया गया है कि व्युत्पन्न वर्ग को बेस कक्षाओं की कार्यक्षमता का विस्तार करना चाहिए , इसे कभी भी प्रतिबंधित नहीं करना चाहिए । अन्यथा, बेस क्लास का उपयोग पॉलीमॉर्फिक रूप से नहीं किया जा सकता है।
यहां तक कि अगर आप विरासत का उपयोग कर सकते हैं, तो इसका मतलब यह नहीं है कि आपको रचना का उपयोग करना हमेशा एक विकल्प होता है। वंशानुक्रम एक शक्तिशाली उपकरण है जो निहित कोड का पुन: उपयोग और गतिशील प्रेषण की अनुमति देता है, लेकिन यह कुछ नुकसान के साथ आता है, यही वजह है कि रचना अक्सर पसंद की जाती है। विरासत और रचना के बीच का व्यापार- उतार स्पष्ट नहीं है, और मेरी राय में lcn के उत्तर में सबसे अच्छा समझाया गया है ।
अंगूठे के एक नियम के रूप में, मैं रचना पर विरासत का चयन करता हूं, जब बहुरूपिक उपयोग बहुत सामान्य होने की उम्मीद होती है, इस मामले में गतिशील प्रेषण की शक्ति बहुत अधिक पठनीय और सुरुचिपूर्ण एपीआई हो सकती है। उदाहरण के लिए, Widget
GUI चौखटे में एक बहुरूपी वर्ग , या Node
XML पुस्तकालयों में एक बहुरूपी वर्ग होने की अनुमति देता है एक एपीआई जो बहुत अधिक पठनीय और सहज ज्ञान युक्त उपयोग की तुलना में क्या आप शुद्ध रूप से रचना के आधार पर होगा।
जैसा कि आप जानते हैं, यह निर्धारित करने के लिए उपयोग की जाने वाली एक अन्य विधि कि वंशानुक्रम संभव है, लिस्कोव प्रतिस्थापन सिद्धांत कहलाता है :
आधार वर्गों के लिए संकेत या संदर्भ का उपयोग करने वाले कार्य यह जानने के बिना व्युत्पन्न वर्गों की वस्तुओं का उपयोग करने में सक्षम होना चाहिए
अनिवार्य रूप से, इसका मतलब यह है कि विरासत संभव है यदि आधार वर्ग का उपयोग पॉलीमॉर्फिक रूप से किया जा सकता है, जो मुझे लगता है कि हमारे परीक्षण के बराबर है "एक बार एक फू है और बार सब कुछ कर सकते हैं जो कि फोओस कर सकते हैं"।
computeArea(Circle* c) { return pi * square(c->radius()); }
। यह स्पष्ट रूप से टूटा हुआ है अगर एक एलीप्स (क्या त्रिज्या () भी मतलब है?) पारित कर दिया। एक दीर्घवृत्त एक सर्कल नहीं है, और जैसे सर्कल से व्युत्पन्न नहीं होना चाहिए।
computeArea(Circle *c) { return pi * width * height / 4.0; }
अब यह सामान्य है।
width()
और height()
? क्या होगा अगर अब एक लाइब्रेरी उपयोगकर्ता "एगशैप" नामक एक अन्य वर्ग बनाने का फैसला करता है? क्या इसे "सर्कल" से भी प्राप्त किया जाना चाहिए? बिलकूल नही। एक अंडा-आकार एक सर्कल नहीं है, और एक अंडाकार भी एक सर्कल नहीं है, इसलिए किसी को भी सर्कल से प्राप्त नहीं करना चाहिए क्योंकि यह एलएसपी है। एक सर्कल * वर्ग पर ऑपरेशन करने वाले तरीके एक सर्कल के बारे में मजबूत धारणा बनाते हैं, और इन धारणाओं को तोड़ने से लगभग निश्चित रूप से कीड़े पैदा होंगे।
वंशानुक्रम बहुत शक्तिशाली है, लेकिन आप इसे मजबूर नहीं कर सकते हैं (देखें: सर्कल-दीर्घवृत्त समस्या )। यदि आप वास्तव में पूरी तरह से एक सच्चे के बारे में सुनिश्चित नहीं हो सकते हैं "- एक" उपप्रकार संबंध है, तो रचना के साथ जाना सबसे अच्छा है।
वंशानुक्रम एक उपवर्ग और सुपर क्लास के बीच एक मजबूत संबंध बनाता है; उपवर्ग को सुपर क्लास के कार्यान्वयन विवरण के बारे में पता होना चाहिए। सुपर क्लास बनाना बहुत कठिन है, जब आपको यह सोचना होगा कि इसे कैसे बढ़ाया जा सकता है। आपको वर्ग के आक्रमणकारियों को सावधानीपूर्वक दस्तावेज़ करना होगा, और यह बताना होगा कि अन्य तरीकों से अधिक उपयोग करने योग्य तरीके आंतरिक रूप से क्या हैं।
वंशानुक्रम कभी-कभी उपयोगी होता है, यदि पदानुक्रम वास्तव में एक-ए-संबंध का प्रतिनिधित्व करता है। यह ओपन-क्लोज्ड सिद्धांत से संबंधित है, जिसमें कहा गया है कि कक्षाओं को संशोधन के लिए बंद किया जाना चाहिए लेकिन विस्तार के लिए खुला होना चाहिए। इस तरह से आपको बहुरूपता हो सकता है; एक सामान्य विधि है जो सुपर प्रकार और इसकी विधियों से संबंधित है, लेकिन गतिशील प्रेषण के माध्यम से उपवर्ग की विधि को लागू किया जाता है। यह लचीला है, और अप्रत्यक्ष बनाने में मदद करता है, जो सॉफ्टवेयर में आवश्यक है (कार्यान्वयन विवरण के बारे में कम जानने के लिए)।
वंशानुक्रम आसानी से अधिक उपयोग किया जाता है, हालांकि, और कक्षाओं के बीच कठिन निर्भरता के साथ, अतिरिक्त जटिलता पैदा करता है। यह भी समझना कि किसी प्रोग्राम के निष्पादन के दौरान क्या होता है, लेयर और मेथड कॉल के गतिशील चयन के कारण बहुत कठिन हो जाता है।
मैं डिफ़ॉल्ट के रूप में रचना का उपयोग करने का सुझाव दूंगा। यह अधिक मॉड्यूलर है, और देर से बाध्यकारी का लाभ देता है (आप घटक को गतिशील रूप से बदल सकते हैं)। इसके अलावा चीजों को अलग से परखना आसान है। और अगर आपको कक्षा से किसी विधि का उपयोग करने की आवश्यकता है, तो आप कुछ निश्चित रूप (लिस्कोव सबस्टीट्यूशन सिद्धांत) के लिए मजबूर नहीं होते हैं।
Inheritance is sometimes useful... That way you can have polymorphism
को विरासत और बहुरूपता की अवधारणा को कड़ी से कड़ी के रूप में व्याख्या की (संदर्भ दिया गया उपप्रकार ग्रहण किया)। मेरी टिप्पणी का उद्देश्य यह बताना था कि आप अपनी टिप्पणी में क्या स्पष्ट करते हैं: कि विरासत में बहुरूपता को लागू करने का एकमात्र तरीका नहीं है, और वास्तव में रचना और विरासत के बीच निर्णय लेते समय निश्चित रूप से निर्धारण कारक नहीं है।
मान लीजिए कि एक विमान में केवल दो भाग होते हैं: एक इंजन और पंख।
फिर एयरक्राफ्ट क्लास डिजाइन करने के दो तरीके हैं।
Class Aircraft extends Engine{
var wings;
}
अब आपका विमान स्थिर पंखों के साथ शुरू हो सकता है
और उन्हें मक्खी पर रोटरी पंखों में बदल सकता है। यह अनिवार्य रूप
से पंखों वाला इंजन है। लेकिन क्या होगा अगर मैं
मक्खी पर भी इंजन बदलना चाहता हूं ?
या तो आधार वर्ग Engine
अपने
गुणों को बदलने के लिए एक उत्परिवर्ती को उजागर करता है , या मैं इसे फिर से डिज़ाइन Aircraft
करता हूं :
Class Aircraft {
var wings;
var engine;
}
अब, मैं अपने इंजन को फ्लाई पर भी बदल सकता हूं।
आपको क्लास डिजाइन के अंकल बॉब के ठोस सिद्धांतों में Liskov प्रतिस्थापन सिद्धांत पर एक नज़र डालने की आवश्यकता है । :)
जब आप बेस क्लास के एपीआई को "कॉपी" / एक्सपोज करना चाहते हैं, तो आप इनहेरिटेंस का उपयोग करते हैं। जब आप केवल कार्यक्षमता को "कॉपी" करना चाहते हैं, तो प्रतिनिधिमंडल का उपयोग करें।
इसका एक उदाहरण: आप एक सूची से एक स्टैक बनाना चाहते हैं। स्टैक में केवल पॉप, पुश और झांकना होता है। आपको दिए गए उत्तराधिकार का उपयोग नहीं करना चाहिए कि आप एक स्टैक में push_back, push_front, removeAt, et al. तरह की कार्यक्षमता नहीं चाहते हैं।
ये दो तरीके बस एक साथ ठीक रह सकते हैं और वास्तव में एक दूसरे का समर्थन करते हैं।
रचना बस इसे मॉड्यूलर खेल रही है: आप मूल वर्ग के समान इंटरफ़ेस बनाते हैं, नई ऑब्जेक्ट बनाते हैं और इसे कॉल करते हैं। यदि इन वस्तुओं को एक दूसरे के बारे में जानने की आवश्यकता नहीं है, तो यह संरचना का उपयोग करने के लिए काफी सुरक्षित और आसान है। यहाँ बहुत सारे कब्ज़े हैं।
हालाँकि, अगर किसी कारण से अभिभावक वर्ग को अनुभवहीन प्रोग्रामर के लिए "चाइल्ड क्लास" द्वारा प्रदान किए गए कार्यों का उपयोग करने की आवश्यकता होती है, तो ऐसा लग सकता है कि यह विरासत का उपयोग करने के लिए एक शानदार जगह है। अभिभावक वर्ग इसे केवल अपना सार "फू" () कह सकता है, जो उपवर्ग द्वारा अधिलेखित है और फिर यह सार आधार को मूल्य दे सकता है।
यह एक अच्छा विचार लगता है, लेकिन कई मामलों में यह बेहतर है कि कक्षा को एक वस्तु दें जो फू () को लागू करता है (या यहां तक कि फू को प्रदान करता है) मैन्युअल रूप से कुछ बेस क्लास से नए वर्ग को प्राप्त करने की तुलना में जो आवश्यक है फ़ंक्शन फू () निर्दिष्ट किया जाना है।
क्यों?
क्योंकि विरासत जानकारी स्थानांतरित करने का एक खराब तरीका है ।
यहां रचना का एक वास्तविक किनारा है: रिश्ते को उलटा किया जा सकता है: "माता-पिता वर्ग" या "अमूर्त कार्यकर्ता" किसी भी विशिष्ट "बच्चे" वस्तुओं को कुछ इंटरफ़ेस को लागू कर सकते हैं + किसी भी बच्चे को किसी अन्य प्रकार के माता-पिता के अंदर सेट किया जा सकता है, जो स्वीकार करता है यह प्रकार है । और किसी भी संख्या में वस्तुएं हो सकती हैं, उदाहरण के लिए मर्जसॉर्ट या क्विकॉर्ट एक अमूर्त तुलना -डंडफेस लागू करने वाली वस्तुओं की किसी भी सूची को सॉर्ट कर सकता है। या इसे दूसरे तरीके से रखने के लिए: वस्तुओं का कोई समूह जो "फू ()" और वस्तुओं के अन्य समूह को लागू करता है जो "फू ()" वाली वस्तुओं का उपयोग एक साथ कर सकते हैं।
मैं वंशानुक्रम का उपयोग करने के तीन वास्तविक कारणों के बारे में सोच सकता हूं:
यदि ये सच हैं, तो संभवतः विरासत का उपयोग करना आवश्यक है।
कारण 1 का उपयोग करने में कुछ भी बुरा नहीं है, आपकी वस्तुओं पर एक ठोस इंटरफ़ेस होना बहुत अच्छी बात है। यह रचना का उपयोग करके या विरासत के साथ किया जा सकता है, कोई समस्या नहीं - अगर यह इंटरफ़ेस सरल है और नहीं बदलता है। आमतौर पर वंशानुक्रम यहां काफी प्रभावी है।
यदि कारण संख्या 2 है तो यह थोड़ा मुश्किल हो जाता है। क्या आपको वास्तव में केवल उसी आधार वर्ग का उपयोग करने की आवश्यकता है? सामान्य तौर पर, बस एक ही आधार वर्ग का उपयोग करना पर्याप्त नहीं है, लेकिन यह आपके ढांचे की आवश्यकता हो सकती है, एक डिजाइन विचार जिसे टाला नहीं जा सकता है।
हालाँकि, यदि आप निजी चर, केस 3 का उपयोग करना चाहते हैं, तो आप मुश्किल में पड़ सकते हैं। यदि आप वैश्विक चर को असुरक्षित मानते हैं, तो आपको विरासत का उपयोग करने पर विचार करना चाहिए ताकि निजी चर भी असुरक्षित हो । ध्यान रखें, वैश्विक चर सभी बुरे नहीं होते हैं - डेटाबेस अनिवार्य रूप से वैश्विक चर का बड़ा समूह होते हैं। लेकिन अगर आप इसे संभाल सकते हैं, तो यह काफी ठीक है।
नए प्रोग्रामर्स के लिए इस प्रश्न को एक अलग दृष्टिकोण से संबोधित करने के लिए:
जब हम ऑब्जेक्ट-ओरिएंटेड प्रोग्रामिंग सीखते हैं तो इनहेरिटेंस अक्सर जल्दी सिखाई जाती है, इसलिए इसे एक सामान्य समस्या के आसान समाधान के रूप में देखा जाता है।
मेरे पास तीन वर्ग हैं जो सभी को कुछ सामान्य कार्यक्षमता की आवश्यकता है। इसलिए यदि मैं एक बेस क्लास लिखता हूं और उन सभी को इसमें से विरासत में मिला है, तो उन सभी में यह कार्यक्षमता होगी और मुझे केवल एक बार इसे बनाए रखने की आवश्यकता होगी।
यह बहुत अच्छा लगता है, लेकिन व्यवहार में यह लगभग कभी नहीं, कभी भी, कई कारणों में से एक काम करता है:
अंत में, हम अपने कोड को कुछ कठिन समुद्री मील में बाँधते हैं और इससे कोई लाभ नहीं मिलता है, सिवाय इसके कि हमें यह कहने के लिए मिलता है, "कूल, मुझे विरासत के बारे में पता चला और अब मैंने इसका इस्तेमाल किया।" इसका मतलब यह नहीं है कि कृपालु होने के कारण हमने यह सब किया है। लेकिन हम सबने ऐसा किया क्योंकि किसी ने हमें नहीं बताया।
जैसे ही किसी ने मुझे "वंशानुक्रम पर अनुकूल रचना" के बारे में समझाया, मैंने सोचा कि हर बार जब मैंने विरासत का उपयोग करके कक्षाओं के बीच कार्यक्षमता साझा करने का प्रयास किया और महसूस किया कि अधिकांश समय यह वास्तव में अच्छी तरह से काम नहीं करता था।
मारक एकल जिम्मेदारी सिद्धांत है । इसे एक बाधा के रूप में सोचें। मेरी कक्षा को एक काम करना चाहिए । मुझे अपनी कक्षा को एक ऐसा नाम देने में सक्षम होना चाहिए जो किसी भी तरह का वर्णन करता है कि वह एक काम करता है। (हर चीज के अपवाद हैं, लेकिन जब हम सीख रहे होते हैं तो निरपेक्ष नियम कभी-कभी बेहतर होते हैं।) यह इस प्रकार है कि मैं बेस क्लास नहीं लिख सकता ObjectBaseThatContainsVariousFunctionsNeededByDifferentClasses
। मुझे जो भी विशिष्ट कार्यक्षमता की आवश्यकता है, वह अपने स्वयं के वर्ग में होनी चाहिए, और फिर अन्य वर्गों को जो उस कार्यक्षमता की आवश्यकता होती है, वह उस वर्ग पर निर्भर कर सकती है, न कि उससे विरासत में।
ओवरसाइप्लाइज़िंग के जोखिम पर, वह रचना है - एक साथ काम करने के लिए कई वर्गों की रचना। और एक बार जब हम उस आदत को बनाते हैं तो हम पाते हैं कि यह विरासत के उपयोग की तुलना में बहुत अधिक लचीला, बनाए रखने योग्य और परीक्षण योग्य है।
एक तरफ से एक / एक विचार है, किसी को भी अपनी वस्तु के माध्यम से जाने की विरासत की "गहराई" पर विचार करना चाहिए। वंशानुक्रम के पाँच या छः स्तरों से परे कुछ भी अनपेक्षित कास्टिंग और बॉक्सिंग / अनबॉक्सिंग समस्याओं का कारण हो सकता है, और उन मामलों में आपकी वस्तु की रचना करना बुद्धिमानी हो सकती है।
जब आपके पास एक -ए है दो वर्गों के बीच संबंध होता है (उदाहरण के लिए कुत्ता एक कुत्ते है), तो आप विरासत के लिए जाते हैं।
दूसरी ओर जब आपके पास दो कक्षाओं (छात्र के पाठ्यक्रम) या (शिक्षक अध्ययन पाठ्यक्रम) के बीच एक या कुछ विशेषण संबंध होते हैं, तो आपने रचना को चुना।
इसका एक सरल तरीका यह होगा कि वंशानुक्रम का उपयोग तब किया जाना चाहिए जब आपको अपनी कक्षा की एक वस्तु की आवश्यकता हो, जिसमें उसके मूल वर्ग के समान इंटरफ़ेस हो , ताकि उसे मूल वर्ग की वस्तु के रूप में माना जा सके (उत्थान) । इसके अलावा, एक व्युत्पन्न वर्ग वस्तु पर फ़ंक्शन कॉल कोड में हर जगह समान रहेगा, लेकिन कॉल करने की विशिष्ट विधि रनटाइम पर निर्धारित की जाएगी (यानी निम्न-स्तरीय कार्यान्वयन अलग - अलग, उच्च-स्तरीय इंटरफ़ेस समान रहता है)।
रचना का उपयोग तब किया जाना चाहिए जब आपको एक ही इंटरफ़ेस रखने के लिए नए वर्ग की आवश्यकता नहीं होती है, अर्थात आप कक्षा के कार्यान्वयन के कुछ पहलुओं को छिपाना चाहते हैं जिसके बारे में उस वर्ग के उपयोगकर्ता को जानकारी नहीं होनी चाहिए। इसलिए रचना एन्कैप्सुलेशन (यानी कार्यान्वयन को छिपाना) का समर्थन करने के तरीके में अधिक है, जबकि विरासत का मतलब अमूर्तता का समर्थन करने के लिए है (यानी किसी चीज़ का सरलीकृत प्रतिनिधित्व प्रदान करना, इस मामले में विभिन्न इंटर्नल के साथ प्रकारों की एक ही इंटरफ़ेस के लिए)।
सबटिप्पिंग उपयुक्त और अधिक शक्तिशाली है जहां अपरिवर्तनीयों को गणना की जा सकती है , अन्यथा विस्तार के लिए फ़ंक्शन रचना का उपयोग करें।
मैं @ फावले से सहमत हूं, जब वह कहता है, रचना के लिए जगह हैं और विरासत के लिए जगह हैं।
मुझे लगता है कि विरासत का उपयोग किया जाना चाहिए, यदि आपका उत्तर इनमें से किसी भी प्रश्न के लिए सकारात्मक है।
हालांकि, यदि आपका इरादा विशुद्ध रूप से कोड री-यूज का है, तो रचना की संभावना सबसे बेहतर डिजाइन विकल्प है।
कोड के पुन: उपयोग के लिए वंशानुक्रम एक बहुत शक्तिशाली 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);
}
}
}
पूर्णांक वर्ग का हमारा सेट इंटेगर की सूची का एक उपवर्ग है, लेकिन यह उपप्रकार नहीं है, क्योंकि यह सूची वर्ग की सभी विशेषताओं को संतुष्ट नहीं करता है। मान, और विधियों के हस्ताक्षर संतुष्ट हैं, लेकिन गुण नहीं हैं। ऐड (इंटेगर) पद्धति के व्यवहार को स्पष्ट रूप से बदल दिया गया है, अभिभावक प्रकार के गुणों को संरक्षित नहीं करना। अपनी कक्षाओं के ग्राहक के दृष्टिकोण से सोचें। उन्हें एक पूर्णांक प्राप्त हो सकता है जहां पूर्णांकों की सूची अपेक्षित है। ग्राहक एक मूल्य जोड़ना चाहते हैं और उस मूल्य को सूची में जोड़ सकते हैं, भले ही वह मूल्य सूची में पहले से मौजूद हो। लेकिन उसका अभिप्राय उस व्यवहार से नहीं है, यदि मूल्य मौजूद है। उसके लिए एक बड़ा आश्चर्य!
यह वंशानुक्रम के अनुचित उपयोग का एक उत्कृष्ट उदाहरण है। इस मामले में रचना का उपयोग करें।
(एक टुकड़ा: से विरासत का उपयोग ठीक से )।
अंगूठे का एक नियम जो मैंने सुना है कि विरासत का उपयोग तब किया जाना चाहिए जब इसका "ए-ए" संबंध और रचना जब इसका "ए-ए" हो। यहां तक कि इसके साथ मुझे लगता है कि आपको हमेशा रचना की ओर झुकना चाहिए क्योंकि यह बहुत जटिलता को समाप्त करता है।
रचना v / s वंशानुक्रम एक विस्तृत विषय है। जो कुछ भी बेहतर है उसके लिए कोई वास्तविक उत्तर नहीं है क्योंकि मुझे लगता है कि यह सब सिस्टम के डिजाइन पर निर्भर करता है।
आम तौर पर वस्तु के बीच संबंध के प्रकार उनमें से किसी एक को चुनने के लिए बेहतर जानकारी प्रदान करते हैं।
यदि संबंध प्रकार "आईएस-ए" संबंध है तो वंशानुक्रम बेहतर दृष्टिकोण है। अन्यथा संबंध प्रकार "एचएएस-ए" संबंध है तो रचना बेहतर दृष्टिकोण करेगी।
यह पूरी तरह से इकाई संबंधों पर निर्भर करता है।
भले ही रचना पसंद किया जाता है, मैं के पेशेवरों को उजागर करना चाहते हैं वंशानुक्रम और के विपक्ष संरचना ।
वंशानुक्रम के नियम:
यह एक तार्किक " आईएस ए" संबंध स्थापित करता है । यदि कार और ट्रक दो प्रकार के वाहन (बेस क्लास) हैं, तो चाइल्ड क्लास आईएस ए बेस क्लास है।
अर्थात
कार एक वाहन है
ट्रक एक वाहन है
विरासत के साथ, आप एक क्षमता को परिभाषित / संशोधित / विस्तारित कर सकते हैं
रचना की विधि:
उदा। यदि कार में वाहन शामिल है और यदि आपको कार की कीमत प्राप्त करनी है , जिसे वाहन में परिभाषित किया गया है , तो आपका कोड इस तरह होगा
class Vehicle{
protected double getPrice(){
// return price
}
}
class Car{
Vehicle vehicle;
protected double getPrice(){
return vehicle.getPrice();
}
}
जैसा कि कई लोगों ने बताया, मैं सबसे पहले चेक से शुरू करूंगा - चाहे "ए-ए" संबंध मौजूद हो। यदि यह मौजूद है तो मैं आमतौर पर निम्नलिखित की जांच करता हूं:
क्या आधार वर्ग को त्वरित किया जा सकता है। यही है, क्या आधार वर्ग गैर-सार हो सकता है। यदि यह गैर-सार हो सकता है तो मैं आमतौर पर रचना पसंद करता हूं
उदाहरण 1. लेखाकार एक कर्मचारी है। लेकिन मैं वंशानुक्रम का उपयोग नहीं करूंगा क्योंकि एक कर्मचारी वस्तु को तत्काल किया जा सकता है।
उदा 2. पुस्तक एक सेलिंग इट है। एक SellItem त्वरित नहीं किया जा सकता है - यह अमूर्त अवधारणा है। इसलिए मैं विरासत का उपयोग करूंगा। सेलिंग इटेम एक सार आधार वर्ग है (या C # में इंटरफ़ेस )
आप इस दृष्टिकोण के बारे में क्या सोचते हैं?
इसके अलावा, मैं @anon उत्तर का समर्थन करता हूं कि वंशानुक्रम का उपयोग क्यों करें?
वंशानुक्रम का उपयोग करने का मुख्य कारण रचना का एक रूप नहीं है - ऐसा इसलिए है ताकि आप बहुरूपी व्यवहार प्राप्त कर सकें। यदि आपको बहुरूपता की आवश्यकता नहीं है, तो आपको शायद विरासत का उपयोग नहीं करना चाहिए।
@MatthieuM। /software/12439/code-smell-inheritance-abuse/12448#comment303759_12448 में कहते हैं
विरासत के साथ मुद्दा यह है कि इसका उपयोग दो ऑर्थोगोनल उद्देश्यों के लिए किया जा सकता है:
इंटरफ़ेस (बहुरूपता के लिए)
कार्यान्वयन (कोड पुन: उपयोग के लिए)
संदर्भ
मुझे लगता है कि किसी ने भी हीरे की समस्या का उल्लेख नहीं किया है , जो विरासत के साथ उत्पन्न हो सकता है।
एक नज़र में, यदि कक्षाएं B और C, A और दोनों को ओवरराइड करने की विधि X, और चतुर्थ श्रेणी D, B और C दोनों से विरासत में मिली हैं, और X को ओवरराइड नहीं करती हैं, तो XD के किस कार्यान्वयन का उपयोग करना चाहिए?
विकिपीडिया इस प्रश्न में चर्चा किए जा रहे विषय का एक अच्छा अवलोकन प्रदान करता है।