एक वर्ग को क्यों विरासत में मिला और गुणों को नहीं जोड़ा?


39

मुझे हमारे (बल्कि बड़े) कोड बेस में एक विरासत का पेड़ मिला जो कुछ इस तरह से होता है:

public class NamedEntity
{
    public int Id { get; set; }
    public string Name { get; set; }
}

public class OrderDateInfo : NamedEntity { }

मैं जो इकट्ठा कर सकता था, यह मुख्य रूप से फ्रंट-एंड पर सामान बांधने के लिए उपयोग किया जाता है।

मेरे लिए, यह समझ में आता है क्योंकि यह सामान्य पर भरोसा करने के बजाय कक्षा को एक ठोस नाम देता है NamedEntity। दूसरी ओर, ऐसी कई कक्षाएं हैं जिनमें बस कोई अतिरिक्त गुण नहीं है।

क्या इस दृष्टिकोण में कोई कमी है?


25
उल्टा उल्लेख नहीं किया गया है: आप उन तरीकों को अलग कर सकते हैं जो केवल OrderDateInfoउन लोगों से एस के लिए प्रासंगिक हैं जो अन्य NamedEntityएस के लिए प्रासंगिक हैं
केलथ

8
उसी कारण से कि यदि आपके पास ऐसा हुआ है, तो कहें, एक ऐसा Identifierवर्ग जिसका कोई लेना-देना नहीं है, NamedEntityलेकिन संपत्ति Idऔर Nameसंपत्ति दोनों की आवश्यकता है, आप NamedEntityइसके बजाय उपयोग नहीं करेंगे । एक वर्ग का संदर्भ और उचित उपयोग केवल उन गुणों और विधियों से अधिक है जो इसे धारण करते हैं।
नील

जवाबों:


15

मेरे लिए, यह समझ में आता है क्योंकि यह सामान्य नामांकितता पर भरोसा करने के बजाय कक्षा को एक ठोस नाम देता है। दूसरी ओर, ऐसी कई कक्षाएं हैं जिनमें बस कोई अतिरिक्त गुण नहीं है।

क्या इस दृष्टिकोण में कोई कमी है?

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

उदाहरण के लिए, विचार करें कि आपने संस्थाओं और नामित संस्थाओं का नाम दिया है। आपके पास कई संस्थाएँ हैं:

Oneऑडिटेड इकाई नहीं है और न ही नाम वाली संस्था है। यह आसान है:

public class One 
{ }

Twoएक नामित संस्था है, लेकिन एक ऑडिटेड इकाई नहीं है। अनिवार्य रूप से अब आपके पास क्या है:

public class NamedEntity
{
    public int Id { get; set; }
    public string Name { get; set; }
}

public class Two : NamedEntity 
{ }

Threeएक नामित और अंकेक्षित दोनों प्रविष्टि है। यह वह जगह है जहाँ आप एक समस्या में भागते हैं। आप एक बना सकते हैं AuditedEntityआधार वर्ग है, लेकिन आप नहीं कर सकते हैं Threeइनहेरिट दोनों AuditedEntity और NamedEntity :

public class AuditedEntity
{
    public DateTime CreatedOn { get; set; }
    public DateTime UpdatedOn { get; set; }
}

public class Three : NamedEntity, AuditedEntity  // <-- Compiler error!
{ }

हालाँकि, आप AuditedEntityइनहेरिट करने से वर्कअराउंड के बारे में सोच सकते हैं NamedEntity। यह सुनिश्चित करने के लिए एक चतुर हैक है कि हर वर्ग को केवल एक दूसरे वर्ग से विरासत (सीधे) की आवश्यकता है।

public class AuditedEntity : NamedEntity
{
    public DateTime CreatedOn { get; set; }
    public DateTime UpdatedOn { get; set; }
}

public class Three : AuditedEntity
{ }

यह अभी भी काम करता है। लेकिन आपने जो यहां किया है वह यह बताया गया है कि प्रत्येक ऑडिटेड इकाई स्वाभाविक रूप से एक नामित इकाई भी है । जो मुझे मेरे अंतिम उदाहरण के लिए लाता है। Fourएक ऑडिटेड इकाई है, लेकिन नामित इकाई नहीं है। लेकिन आप Fourसे विरासत में मिलने नहीं दे सकते AuditedEntityक्योंकि तब आप NamedEntityऑडिटेडइनिटी andनामांकितता के बीच विरासत के कारण भी इसे बना रहे होंगे।

वंशानुक्रम का उपयोग करना, दोनों को बनाने Threeऔर Fourकाम करने का कोई तरीका नहीं है जब तक कि आप डुप्लिकेटिंग कक्षाएं शुरू नहीं करते हैं (जो समस्याओं का एक नया सेट खोल देता है)।

इंटरफेस का उपयोग करते हुए, यह आसानी से प्राप्त किया जा सकता है:

public interface INamedEntity
{
    int Id { get; set; }
    string Name { get; set; }
}

public interface IAuditedEntity
{
    DateTime CreatedOn { get; set; }
    DateTime UpdatedOn { get; set; }
}

public class One 
{ }

public class Two : INamedEntity 
{
    public int Id { get; set; }
    public string Name { get; set; }
}

public class Three : INamedEntity, IAuditedEntity
{
    public int Id { get; set; }
    public string Name { get; set; }
    DateTime CreatedOn { get; set; }
    DateTime UpdatedOn { get; set; }
}

public class Four : IAuditedEntity
{
    DateTime CreatedOn { get; set; }
    DateTime UpdatedOn { get; set; }
}

यहां केवल मामूली खामी यह है कि आपको अभी भी इंटरफ़ेस को लागू करना है। लेकिन आपको एक सामान्य पुन: प्रयोज्य प्रकार होने से सभी लाभ मिलते हैं, बिना किसी कमियों के जो किसी भी इकाई के लिए कई सामान्य प्रकारों पर विविधताओं की आवश्यकता होती है।

लेकिन आपका बहुरूपता बरकरार है:

var one = new One();
var two = new Two();
var three = new Three();
var four = new Four();

public void HandleNamedEntity(INamedEntity namedEntity) {}
public void HandleAuditedEntity(IAuditedEntity auditedEntity) {}

HandleNamedEntity(one);    //Error - not a named entity
HandleNamedEntity(two);
HandleNamedEntity(three);  
HandleNamedEntity(four);   //Error - not a named entity

HandleAuditedEntity(one);    //Error - not an audited entity
HandleAuditedEntity(two);    //Error - not an audited entity
HandleAuditedEntity(three);  
HandleAuditedEntity(four);

दूसरी ओर, ऐसी कई कक्षाएं हैं जिनमें बस कोई अतिरिक्त गुण नहीं है।

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

अंकित मूल्य पर, मार्कर इंटरफेस / कक्षाओं के साथ कुछ भी गलत नहीं है। वे वाक्यात्मक रूप से और तकनीकी रूप से मान्य हैं, और उनका उपयोग करने के लिए कोई अंतर्निहित कमियां नहीं हैं बशर्ते कि मार्कर सार्वभौमिक रूप से सच हो (संकलन के समय) और सशर्त नहीं

ठीक यही है कि आपको अलग-अलग अपवादों के बीच अंतर करना चाहिए, तब भी जब उन अपवादों में आधार विधि की तुलना में कोई अतिरिक्त गुण / विधियां नहीं होती हैं।

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


4
"आप केवल एक वर्ग से वारिस हो सकते हैं, लेकिन आप कई इंटरफेस लागू कर सकते हैं।" हालांकि यह हर भाषा का सच नहीं है। कुछ भाषाएं कई वर्ग की विरासत की अनुमति देती हैं।
केनेथ के।

जैसे केनेथ ने कहा, दो बहुत लोकप्रिय भाषाओं में कई विरासत, C ++ और पायथन की क्षमता है। threeइंटरफेस का उपयोग नहीं करने के नुकसान की आपके उदाहरण में क्लास वास्तव में C ++ में मान्य है उदाहरण के लिए, वास्तव में यह सभी चार उदाहरणों के लिए ठीक से काम करता है। वास्तव में यह सब सी # की एक सीमा के रूप में लगता है एक भाषा के बजाय एक सावधानी की कहानी के बजाय विरासत का उपयोग नहीं करते हैं जब आपको इंटरफेस का उपयोग करना चाहिए।
whn

63

यह कुछ ऐसा है जिसका उपयोग मैं बहुरूपता को रोकने के लिए करता हूं।

मान लीजिए कि आपके पास 15 अलग-अलग वर्ग हैं, जिनके पास NamedEntityअपनी विरासत श्रृंखला में एक आधार वर्ग के रूप में है और आप एक नई विधि लिख रहे हैं जो केवल उसी पर लागू होती है OrderDateInfo

आप केवल हस्ताक्षर लिख सकते हैं

void MyMethodThatShouldOnlyTakeOrderDateInfos(NamedEntity foo)

और आशा करते हैं और प्रार्थना करते हैं कि कोई भी प्रकार प्रणाली को FooBazNamedEntityवहां से हटाने के लिए गाली न दे।

या आप "बस" लिख सकते हैं void MyMethod(OrderDateInfo foo)। अब यह संकलक द्वारा लागू किया गया है। सरल, सुरुचिपूर्ण और लोगों की गलतियों पर भरोसा नहीं करता है।

इसके अलावा, जैसा कि @candied_orange ने बताया , अपवाद इस का एक बड़ा मामला है। बहुत कम ही (और मेरा मतलब है बहुत, बहुत, बहुत कम ही) क्या आप कभी भी सब कुछ पकड़ना चाहते हैं catch (Exception e)। अधिक संभावना है कि आप एक को पकड़ने के लिए चाहते हैं SqlExceptionया एक FileNotFoundExceptionया अपने आवेदन के लिए एक कस्टम अपवाद। वे वर्ग अक्सर आधार Exceptionवर्ग की तुलना में कोई अधिक डेटा या कार्यक्षमता प्रदान नहीं करते हैं , लेकिन वे आपको अंतर करने की अनुमति देते हैं कि वे उनका निरीक्षण किए बिना किस प्रकार का प्रतिनिधित्व करते हैं और एक प्रकार के क्षेत्र की जांच करें या विशिष्ट पाठ की खोज करें।

कुल मिलाकर, यह एक प्रकार की प्रणाली प्राप्त करने की एक तरकीब है जिससे आप आधार वर्ग का उपयोग करने की तुलना में अपने प्रकार के संकरे सेट का उपयोग कर सकते हैं। मेरा मतलब है, आप अपने सभी चर और तर्कों को इस प्रकार के रूप में परिभाषित कर सकते हैं Object, लेकिन यह सिर्फ आपके काम को कठिन बना देगा, नहीं?


4
मेरे नएपन को क्षमा करें, लेकिन मुझे उत्सुकता है कि एक संपत्ति के रूप में ऑर्डरडेमइनो को एक नामांकित वस्तु बनाने के लिए बेहतर दृष्टिकोण क्यों नहीं होगा?
एडम बी

9
@AdamB यह एक वैध डिजाइन विकल्प है। यह बेहतर है या नहीं यह इस बात पर निर्भर करता है कि इसका उपयोग अन्य चीजों के लिए क्या किया जाएगा। अपवाद मामले में, मैं एक अपवाद संपत्ति के साथ एक नया वर्ग नहीं बना सकता क्योंकि तब भाषा (मेरे लिए C #) मुझे इसे फेंकने नहीं देगी। अन्य डिजाइन विचार हो सकते हैं जो विरासत के बजाय रचना का उपयोग करना पसंद करेंगे। कई बार रचना बेहतर होती है। यह सिर्फ आपके सिस्टम पर निर्भर करता है।
बज़्ज़

17
@AdamB इसी कारण से कि जब आप बेस क्लास से वारिस करते हैं और डीओ में ऐसी विधियाँ या गुण जोड़ देते हैं, तो आप एक नया क्लास नहीं बनाते हैं और एक प्रॉपर्टी के रूप में बेस डाल देते हैं। एक मानव स्तनपायी मानव और एक स्तनपायी हवन के बीच अंतर है।
लोगर

8
@ सच तो यह है कि मेरे स्तनपायी होने और स्तनपायी होने में अंतर है। लेकिन अगर मैं अपने भीतर के स्तनपायी के लिए सच रहता हूं, तो आपको कभी भी अंतर नहीं पता चलेगा। आप आश्चर्यचकित हो सकते हैं कि मैं एक फुफकार पर इतने विभिन्न प्रकार के दूध क्यों दे सकता हूं।
कैंडिड_ऑरेंज

5
@ अदमबी आप ऐसा कर सकते हैं, और इसे रचना-पर-विरासत के रूप में जाना जाता है। लेकिन आप इसके लिए नाम बदल देंगे NamedEntity, उदाहरण के लिए EntityName, ताकि रचना का वर्णन होने पर समझ में आए।
सेबस्टियन रेडल

28

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

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


1
हम्म, अच्छी बात फिर से अपवाद। मैं यह कहने जा रहा था कि यह एक भयानक बात है। लेकिन आपने मुझे एक उत्कृष्ट उपयोग के मामले के साथ अन्यथा राजी कर लिया है।
डेविड अरनो

यो-यो हो और रम की एक बोतल। टीस दुर्लभ ऑरलोप यार है। यह उचित ओकम की चिमटी के लिए तख्तों की लीक है। भूमि के साथ डेवी जोन्स लॉकर को लुबर्स ने इसे बनाया। अनुवाद: यह दुर्लभ है कि एक विरासत श्रृंखला इतनी अच्छी है कि आधार वर्ग सार / प्रभावी ढंग से अनदेखा किया जा सकता है।
राडारबोब

मैं इसे की कीमत उनका कहना है कि एक नया वर्ग एक और से इनहेरिट लगता है नया वर्ग के नाम - नई प्रॉपर्टी जोड़ने। यह वही है जो आप बेहतर नामों के साथ अपवाद बनाते समय उपयोग करते हैं।
लोगन पिकअप

1

IMHO वर्ग डिजाइन गलत है। यह होना चाहिए।

public class EntityName
{
    public int Id { get; set; }
    public string Text { get; set; }
}

public class OrderDateInfo
{
    public EntityName Name { get; set; }
}

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

किसी भी विधि NamedEntityको एक पैरामीटर के रूप में स्वीकार किया जाना चाहिए केवल गुणों Idऔर Nameगुणों में रुचि होनी चाहिए , इसलिए इस तरह के तरीकों को स्वीकार EntityNameकरने के लिए बदलना चाहिए ।

मूल डिजाइन के लिए मुझे स्वीकार करने का एकमात्र तकनीकी कारण संपत्ति के बंधन के लिए है, जिसे ओपी ने उल्लेख किया है। एक बकवास ढांचा अतिरिक्त संपत्ति के साथ सामना करने और बांधने में सक्षम नहीं होगा object.Name.Id। लेकिन अगर आपका बाइंडिंग फ्रेमवर्क इसके साथ सामना नहीं कर सकता है तो आपके पास सूची में जोड़ने के लिए कुछ और तकनीकी ऋण हैं।

मैं @ Flater के उत्तर के साथ जाऊंगा, लेकिन बहुत सारे इंटरफेस वाले गुणों के साथ आप बहुत सारे बॉयलरप्लेट कोड लिख रहे हैं, यहां तक ​​कि C # के सुंदर स्वचालित गुणों के साथ भी। यह जावा में करने की कल्पना करो!


1

कक्षाएं व्यवहार को उजागर करती हैं, और डेटा संरचनाएं डेटा को उजागर करती हैं।

मुझे कक्षा के कीवर्ड दिखाई देते हैं, लेकिन मुझे कोई व्यवहार नहीं दिखता है। इसका मतलब है कि मैं इस वर्ग को डेटा संरचना के रूप में देखना शुरू करूंगा। इस नस में, मैं आपके प्रश्न के रूप में जा रहा हूँ

एक सामान्य शीर्ष स्तरीय डेटा संरचना क्यों है?

तो आप शीर्ष स्तर के डेटा प्रकार का उपयोग कर सकते हैं। यह सभी प्रकार के गुणों को सुनिश्चित करके विभिन्न डेटा संरचनाओं के बड़े सेटों में एक नीति सुनिश्चित करने के लिए प्रकार प्रणाली का लाभ उठाने की अनुमति देता है।

एक डेटा संरचना क्यों है जिसमें शीर्ष स्तर एक शामिल है, लेकिन कुछ नहीं जोड़ता है?

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

Top level data structure - Named
   property: name;

Bottom level data structure - Person

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

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

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.