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


10

मेरा एक सहकर्मी बेस क्लास या इंटरफ़ेस बनाने के लिए चुनने के लिए एक नियम-अंगूठे के साथ आया था।

वह कहता है:

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

उदाहरण के लिए:

कक्षाओं पर विचार करें catऔर dog, जो कक्षा का विस्तार करते हैं mammalऔर एक ही विधि है pet()। हम तब वर्ग जोड़ते हैं alligator, जो कुछ भी विस्तार नहीं करता है और एक ही विधि है slither()

अब, हम eat()उन सभी में एक विधि जोड़ना चाहते हैं।

यदि eat()विधि का कार्यान्वयन बिल्कुल उसी के लिए होगा cat, dogऔर alligator, हमें एक बेस क्लास बनाना चाहिए (मान लें कि animal), जो इस पद्धति को लागू करता है।

हालांकि, अगर यह alligatorमामूली तरीके से अलग है, तो हमें एक IEatइंटरफ़ेस बनाना चाहिए mammalऔर alligatorइसे लागू करना चाहिए और इसे लागू करना चाहिए।

वह जोर देकर कहते हैं कि यह तरीका सभी मामलों को कवर करता है, लेकिन यह मेरे लिए अति-सरलीकरण जैसा लगता है।

क्या यह इस नियम का पालन करने योग्य है?


27
alligatoreatभिन्नता का कार्यान्वयन , निश्चित रूप से, इसमें वह स्वीकार करता है catऔर dogमापदंडों के रूप में।
sbichenko

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

2
जब आप अपने आप को एक कोने में रंगते हैं, तो दरवाजे के सबसे पास होना सबसे अच्छा है।
21

10
लोग जो सबसे बड़ी गलती करते हैं, वह यह मानता है कि इंटरफेस केवल खाली अमूर्त वर्ग हैं। एक इंटरफ़ेस प्रोग्रामर के लिए कहने का एक तरीका है, "मुझे परवाह नहीं है कि आप मुझे क्या देते हैं, जब तक कि यह इस सम्मेलन का अनुसरण करता है।" रचना के माध्यम से कोड का पुन: उपयोग किया जाना चाहिए (आदर्श रूप से)। आपका सहकर्मी गलत है।
रिवलॉक १२'१३

2
मेरे एक सीएस प्रोफे ने सिखाया कि सुपरक्लास होना चाहिए is aऔर इंटरफेस हैं acts likeया is। तो एक कुत्ता is aस्तनपायी और acts likeएक भक्षक। यह हमें बताता है कि स्तनपायी एक वर्ग होना चाहिए और खाने वाला एक इंटरफ़ेस होना चाहिए। यह हमेशा एक बहुत ही मददगार मार्गदर्शक रहा है। सिडेनोट: इसका एक उदाहरण या isहोगा । The cake is eatableThe book is writable
मिररफाइंड

जवाबों:


13

मुझे नहीं लगता कि यह अंगूठे का एक अच्छा नियम है। यदि आप कोड के पुन: उपयोग के बारे में चिंतित हैं, तो आप PetEatingBehaviorबिल्लियों और कुत्तों के खाने के कार्यों को परिभाषित करने के लिए कौन सी भूमिका निभा सकते हैं। तब आप IEatएक साथ पुन: उपयोग और कोड कर सकते हैं।

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

यदि आप अपने एपीआई के लिए इंटरफेस करने के लिए छड़ी करते हैं, तो आपके उपयोगकर्ता के पास साधारण उदाहरणों को काम करने के लिए आमतौर पर अधिक काम करना पड़ता है। लेकिन आधार वर्ग की IEatतुलना में कम जानकारी वाले सरल कारण को बनाए रखने के लिए इंटरफेस के साथ एपीआई अक्सर कम युग्मित और आसान होते हैं Mammal। इसलिए उपभोक्ता एक कमजोर अनुबंध पर निर्भर होंगे, आपको अधिक रिफैक्टरिंग के अवसर मिलते हैं, आदि।

रिच हिकी को उद्धृत करने के लिए: सरल! = आसान


क्या आप PetEatingBehaviorकार्यान्वयन का एक मूल उदाहरण प्रदान कर सकते हैं ?
sbichenko

1
@exizt सबसे पहले, मैं नाम चुनने में बुरा हूँ। तो PetEatingBehaviorशायद गलत है। मैं आपको एक कार्यान्वयन नहीं दे सकता क्योंकि यह केवल एक खिलौना उदाहरण है। लेकिन मैं रिफैक्टिंग चरणों का वर्णन कर सकता हूं: इसमें एक कंस्ट्रक्टर है जो eatविधि (दांतों की आवृत्ति, पेट की आवृत्ति, आदि) को परिभाषित करने के लिए बिल्लियों / कुत्तों द्वारा उपयोग की जाने वाली सभी जानकारी लेता है । इसमें केवल बिल्लियों और कुत्तों के लिए उनके eatपद्धति में उपयोग किए जाने वाले कोड शामिल हैं । आपको बस एक PetEatingBehaviorसदस्य बनाना होगा और उसे कॉल करना होगा dog.eat/cat.eat को।
साइमन बेरगोट

मैं विश्वास नहीं कर सकता कि यह वंशानुक्रम से दूर ओपी को चलाने के लिए कई का एकमात्र उत्तर है :( वंशानुक्रम कोड पुन: उपयोग के लिए नहीं है!
डैनी टुप्पेनी

1
@DannyTuppeny क्यों नहीं?
एसबीचेंको

4
एक वर्ग से @exizt इनहेरिट करना बड़ी बात है; आप किसी चीज़ पर एक (संभावना-स्थायी) निर्भरता ले रहे हैं, जो कई भाषाओं में आप केवल एक ही हो सकते हैं। कोड पुन: उपयोग इस पर अक्सर-केवल आधार वर्ग का उपयोग करने के लिए एक काफी तुच्छ बात है। क्या होगा यदि आप उन विधियों के साथ समाप्त होते हैं जहां आप उन्हें किसी अन्य वर्ग के साथ साझा करना चाहते हैं, लेकिन पहले से ही अन्य विधियों का पुन: उपयोग करने के कारण इसका आधार वर्ग है (या यहां तक ​​कि, यह एक "वास्तविक" पदानुक्रम का उपयोग किया गया पॉलीमॉर्फ़िकली (?)) का हिस्सा है। इनहेरिटेंस का उपयोग तब करें जब ClassA एक प्रकार की ClassB हो (जैसे कि एक कार वाहन से विरासत में मिली हो), तब नहीं जब यह कुछ आंतरिक कार्यान्वयन विवरणों को साझा करती है :-)
डैनी टुपेंनी

11

क्या यह इस नियम का पालन करने योग्य है?

यह अंगूठे का एक अच्छा नियम है, लेकिन मैं कुछ ऐसी जगहों के बारे में जानता हूं जहां मैं इसका उल्लंघन करता हूं।

निष्पक्ष होने के लिए, मैं अपने साथियों की तुलना में बहुत अधिक बेस क्लास (अमूर्त) का उपयोग करता हूं। मैं इसे रक्षात्मक प्रोग्रामिंग के रूप में करता हूं।

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

इस तरह के डिज़ाइन विकल्प आपके कार्यान्वयनकर्ताओं को इस बारे में संकेत देते हैं कि उन्हें आपके कोड का उपयोग कैसे करना चाहिए, और विस्तार से अपना कोड लिखें। कोडबेस पर उनके अधिक प्रभाव के कारण, मैं डिजाइन निर्णय लेते समय इन बातों को अधिक दृढ़ता से ध्यान में रखता हूं।


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

9

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

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

यहाँ कैसे मेरे एक प्रोफेसर ने अंतर सिखाया:

सुपरक्लास होना चाहिए is aऔर इंटरफेस हैं acts likeया is। तो एक कुत्ता is aस्तनपायी और acts likeएक भक्षक। यह हमें बताता है कि स्तनपायी एक वर्ग होना चाहिए और खाने वाला एक इंटरफ़ेस होना चाहिए। का एक उदाहरण isहोगा The cake is eatableया The book is writable(दोनों बनाने eatableऔर writableइंटरफेस)।

इस तरह की कार्यप्रणाली का उपयोग करना काफी सरल और आसान है, और आपको लगता है कि कोड क्या करेगा, इसके बजाय आपको संरचना और अवधारणाओं को कोड करने का कारण बनता है। यह रखरखाव को आसान बनाता है, कोड को अधिक पठनीय, और डिज़ाइन को सरल बनाता है।

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

केवल मेरे दो सेंट्स।


6

निर्वासन के अनुरोध पर, मैं अपनी टिप्पणी का लंबे समय तक जवाब दे रहा हूं।

लोग जो सबसे बड़ी गलती करते हैं, वह यह मानता है कि इंटरफेस केवल खाली अमूर्त वर्ग हैं। एक इंटरफ़ेस प्रोग्रामर के लिए कहने का एक तरीका है, "मुझे परवाह नहीं है कि आप मुझे क्या देते हैं, जब तक कि यह इस सम्मेलन का अनुसरण करता है।"

.NET पुस्तकालय एक अद्भुत उदाहरण के रूप में कार्य करता है। उदाहरण के लिए, जब आप एक फ़ंक्शन लिखते हैं जो एक स्वीकार करता है IEnumerable<T>, तो आप क्या कह रहे हैं, "मुझे परवाह नहीं है कि आप अपने डेटा को कैसे संग्रहीत कर रहे हैं। मैं सिर्फ यह जानना चाहता हूं कि मैं एक foreachलूप का उपयोग कर सकता हूं ।

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

लेकिन फिर सवाल उठता है: "कोड के पुन: उपयोग के बारे में क्या? मेरे सीएस प्रोफेसरों ने मुझे बताया कि विरासत सभी कोड के पुन: उपयोग की समस्याओं का समाधान था और यह विरासत आपको एक बार लिखने और हर जगह उपयोग करने की अनुमति देती है और यह अनाथों को बचाने की प्रक्रिया में मेनेंजाइटिस का इलाज करेगा। बढ़ते समुद्रों से और अधिक आँसू और पर और आदि आदि नहीं होंगे। "

वंशानुक्रम का उपयोग सिर्फ इसलिए कि आपको "कोड पुन: उपयोग करें" शब्दों की ध्वनि एक बहुत बुरा विचार है। गूगल द्वारा कोड स्टाइल गाइड इस बिंदु काफी संक्षेप में बनाता है:

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

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

FileStreamउदाहरण काल्पनिक लग सकता है, लेकिन यह नहीं है। यदि आपके पास दो कक्षाएं हैं जो दोनों एक ही इंटरफ़ेस को ठीक उसी तरह से लागू करते हैं, तो आपके पास एक तीसरा वर्ग होना चाहिए जो कि किसी भी ऑपरेशन को डुप्लिकेट करता है। आपका लक्ष्य उन वर्गों को लिखना चाहिए जो स्व-युक्त पुन: प्रयोज्य ब्लॉक हैं जिन्हें लेगो की तरह एक साथ रखा जा सकता है।

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

दिन के अंत में, आपके सहकर्मी की भावनाओं में एक सूचित निर्णय लेने के लिए आवश्यक गहराई या समझ का अभाव होता है। विषय पर शोध करें और इसे अपने लिए समझें।

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


इसलिए, अगर मैं आपको सही तरीके से समझता हूं, तो निर्भरता इंजेक्शन विरासत के समान कोड पुन: उपयोग की क्षमता प्रदान करता है। लेकिन आप किसके लिए विरासत का उपयोग करेंगे, तब? क्या आप एक उपयोग-मामला प्रदान करेंगे? (मैं वास्तव में एक के साथ की सराहना की FileStream)
sbichenko

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

4

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

आप इस मामले में इस तरह के अमूर्त व्यवहार से बेहतर हैं:

public interface IFoodEater
{
    void Eat(IFood food);
}

public class Animal : IFoodEater
{
    private IFoodProcessor _foodProcessor;
    public Animal(IFoodProcessor foodProcessor)
    {
        _foodProcessor = foodProcessor;
    }

    public void Eat(IFood food)
    {
        _foodProcessor.Process(food);
    }
}

या एक आगंतुक पैटर्न लागू करें जहां FoodProcessorसंगत जानवरों ( ICarnivore : IFoodEater, MeatProcessingVisitor) को खिलाने की कोशिश की जाती है । जीवित जानवरों के बारे में सोचा जाना आपको उनके पाचन मार्ग को तेज करने और एक सामान्य के साथ बदलने के बारे में सोचने में बाधा डाल सकता है, लेकिन आप वास्तव में उन्हें एक एहसान कर रहे हैं।

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

तो हां, बेस क्लास में अपनी जगह होती है, अंगूठे का नियम लागू होता है (अक्सर, हमेशा नहीं, जैसा कि यह अंगूठे का नियम है), लेकिन व्यवहार की तलाश में रहें जिसे आप अलग कर सकते हैं।


1
IFoodEater बेमानी की तरह है। आप भोजन करने में सक्षम होने के लिए और क्या उम्मीद करते हैं? हाँ, उदाहरण के रूप में जानवर भयानक चीज़ हैं।
युफोरिक

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

1
मेरा मानना ​​है कि "खाना" यहां गलत विचार है: अधिक सार। कैसे एक IConsumerइंटरफ़ेस के बारे में मांसाहारी मांस का उपभोग कर सकते हैं, जड़ी बूटी पौधों का उपभोग करते हैं, और पौधे धूप और पानी का उपभोग करते हैं।

2

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

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

उदाहरण के लिए:

public abstract class Person
    {
        /// <summary>
        /// Inheritors must implement a hello
        /// </summary>
        /// <returns>Hello</returns>
        public abstract string SayHello();
        /// <summary>
        /// Inheritors can use base functionality, or override
        /// </summary>
        /// <returns></returns>
        public virtual string SayGoodBye()
        {
            return "Good Bye";
        }
        /// <summary>
        /// Base Functionality that is inherited 
        /// </summary>
        public void DoSomething()
        {
            //Do Something Here
        }
    }

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

अब यदि "खाने" का दोष पशु से जानवर तक व्यापक रूप से भिन्न होता है तो शायद और इंटरफ़ेस बेहतर होगा। यह कभी-कभी एक ग्रे क्षेत्र होता है और व्यक्तिगत स्थिति पर निर्भर करता है।


1

नहीं।

इंटरफेस इस बारे में हैं कि तरीकों को कैसे लागू किया जाता है। यदि आप अपने फैसले को आधार बना रहे हैं कि जब भी इंटरफ़ेस का उपयोग कैसे किया जाए, तो यह कैसे लागू होगा, तो आप कुछ गलत कर रहे हैं।

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

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


रुको, क्यों हर जानवर eat()में फिर से लागू ? हम इसे लागू कर सकते हैं , हम नहीं कर सकते? मैं आवश्यकताओं के बारे में आपकी बात समझता हूँ, हालाँकि। mammal
sbichenko

@exizt: क्षमा करें, मैंने आपके प्रश्न को गलत तरीके से पढ़ा।
युफोरिक

1

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

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

जब आप एक ही तरीके से कक्षाओं के एक सेट के साथ बातचीत करने की आवश्यकता होती है, तो इंटरफेस उपयोगी होते हैं, लेकिन आप उनके वास्तविक कार्यान्वयन के बारे में अनुमान नहीं लगा सकते हैं या परवाह नहीं कर सकते हैं। उदाहरण के लिए कलेक्शन इंटरफेस जैसा कुछ लें। भगवान केवल सभी असंख्य तरीकों को जानते हैं जो कोई व्यक्ति वस्तुओं के संग्रह को लागू करने का निर्णय ले सकता है। लिंक्ड सूची? सारणी सूची? ढेर? कतार? यह SearchAndReplace विधि ध्यान नहीं देती है, बस इसे कुछ जोड़ने की आवश्यकता होती है जिसे यह Add, Remove, और GetEnorerator पर कॉल कर सकता है।


1

मैं खुद इस सवाल का जवाब देखने के लिए तैयार हूं, यह देखने के लिए कि अन्य लोगों को क्या कहना है, लेकिन मैं तीन चीजें कहूंगा जो इस बारे में सोचने के लिए इच्छुक हूं:

  1. लोग इंटरफेस में बहुत अधिक विश्वास करते हैं, और कक्षाओं में बहुत कम विश्वास करते हैं। आधारभूत वर्गों में इंटरफेस अनिवार्य रूप से बहुत कम होते हैं, और ऐसे अवसर होते हैं जब कुछ हल्का उपयोग करने से अत्यधिक बॉयलरप्लेट कोड जैसी चीजें हो सकती हैं।

  2. किसी पद्धति को ओवरराइड करने, उसके super.method()भीतर कॉल करने और उसके शरीर के बाकी हिस्सों को बस ऐसे काम करने देने में कुछ भी गलत नहीं है जो आधार वर्ग के साथ हस्तक्षेप नहीं करते हैं। यह लिस्कोव प्रतिस्थापन सिद्धांत का उल्लंघन नहीं करता है ।

  3. अंगूठे के एक नियम के रूप में, सॉफ्टवेयर इंजीनियरिंग में यह कठोर और हठधर्मी होना एक बुरा विचार है, भले ही कुछ आम तौर पर सबसे अच्छा अभ्यास हो।

इसलिए मैं नमक के दाने के साथ जो कहा गया था वह ले जाऊंगा।


5
People put way too much faith into interfaces, and too little faith into classes.मजेदार। मुझे लगता है कि विपरीत सच है।
साइमन बर्गोट

1
"यह लिस्कोव प्रतिस्थापन सिद्धांत का उल्लंघन नहीं करता है।" लेकिन यह एलएसपी का उल्लंघन बनने के बेहद करीब है। माफी माँगने से बेहतर है सुरक्षित रहना।
युफोरिक

1

मैं इस ओर एक भाषिक और अर्थपूर्ण दृष्टिकोण रखना चाहता हूं। एक इंटरफ़ेस के लिए नहीं है DRY। हालांकि इसे अमूर्त वर्ग के साथ केंद्रीकरण के लिए एक तंत्र के रूप में इस्तेमाल किया जा सकता है जो इसे लागू करता है, यह DRY के लिए नहीं है।

एक इंटरफ़ेस एक अनुबंध है।

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

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


0

मुझे यकीन नहीं है कि यह अंगूठे का सबसे अच्छा नियम है। आप abstractबेस क्लास में एक विधि रख सकते हैं और प्रत्येक वर्ग इसे लागू कर सकता है। हर mammalखाने के बाद से इसे करने के लिए समझ में आता है। फिर प्रत्येक mammalअपनी एक eatविधि का उपयोग कर सकता है । मैं आमतौर पर केवल वस्तुओं के बीच संचार के लिए इंटरफेस का उपयोग करता हूं, या किसी वर्ग को कुछ के रूप में चिह्नित करने के लिए मार्कर के रूप में। मुझे लगता है कि अतिव्यापी इंटरफेस मैला कोड के लिए बनाता है।

मैं @Panzercrisis से भी सहमत हूँ कि अंगूठे का एक नियम सिर्फ एक सामान्य दिशानिर्देश होना चाहिए। ऐसा कुछ नहीं जो पत्थर में लिखा जाए। निस्संदेह ऐसे समय होंगे जहां यह काम नहीं करता है।


-1

इंटरफेस प्रकार हैं। वे कोई कार्यान्वयन प्रदान करते हैं। वे उस प्रकार से निपटने के लिए अनुबंध को सरल रूप से परिभाषित करते हैं। यह अनुबंध इंटरफ़ेस को लागू करने वाले किसी भी वर्ग द्वारा पूरा किया जाना है।

इंटरफेस और एब्स्ट्रैक्ट / बेस क्लास का उपयोग डिजाइन आवश्यकताओं द्वारा निर्धारित किया जाना चाहिए।

एक एकल वर्ग को इंटरफ़ेस की आवश्यकता नहीं होती है।

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

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

वंशानुक्रम पर अनुकूल रचना - यह सब चला जाता है।

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