2009-04-23 10 views
27

एक इंटरफ़ेस + एक्सटेंशन विधियां (मिश्रण) एक अमूर्त वर्ग के लिए बेहतर है?इंटरफ़ेस + एक्सटेंशन (मिश्रण) बनाम बेस क्लास

यदि आपका उत्तर "यह निर्भर करता है", तो यह किस पर निर्भर करता है?

मुझे इंटरफ़ेस + एक्सटेंशन दृष्टिकोण के दो संभावित फायदे दिखाई देते हैं।

  • इंटरफेस विरासत में गुणा कर रहे हैं और कक्षाएं नहीं हैं।
  • आप एक गैर-ब्रेकिंग तरीके से इंटरफेस बढ़ाने के लिए विस्तार विधियों का उपयोग कर सकते हैं। (आपके इंटरफ़ेस को लागू करने वाले ग्राहक आपके नए आधार कार्यान्वयन को प्राप्त करेंगे लेकिन फिर भी इसे ओवरराइड करने में सक्षम होंगे।)

मैंने अभी तक इस दृष्टिकोण के नकारात्मक पक्ष के बारे में सोचा नहीं है। एक शानदार कारण हो सकता है कि इंटरफ़ेस + एक्सटेंशन दृष्टिकोण विफल हो जाएगा। इस विषय पर

दो मददगार लेख हैं

+1

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

उत्तर

21

विस्तार तरीकों का नकारात्मक पहलू: ग्राहकों पूर्व सी # 3/VB9 रूप में उपयोग करने में सक्षम नहीं होगा आसानी से।

यह इस बारे में है जहां तक ​​मेरा संबंध है - मुझे लगता है कि इंटरफ़ेस-आधारित दृष्टिकोण काफी अच्छा है। फिर आप अपनी निर्भरताओं को अच्छी तरह से मजाक कर सकते हैं, और सबकुछ मूल रूप से कम कसकर जोड़ दिया जाता है। मैं कक्षा विरासत का एक बहुत बड़ा प्रशंसक नहीं हूँ, जब तक यह विशेषज्ञता :)

संपादित करें के बारे में वास्तव में क्या है: मैं सिर्फ एक अन्य लाभ जो प्रासंगिक हो सकता है के बारे में सोचा है। यह संभव है कि कुछ ठोस कार्यान्वयन कुछ सामान्य तरीकों के अधिक अनुकूलित संस्करण प्रदान कर सकें।

Enumerable.Count इस का एक अच्छा उदाहरण है - यह स्पष्ट रूप से जाँच करता है अनुक्रम IList या नहीं लागू करता है या नहीं, क्योंकि अगर यह होता है यह बजाय पूरे अनुक्रम के माध्यम से पुनरावृत्ति की सूची पर Count कॉल कर सकते हैं। यदि IEnumerable<T> वर्चुअल Count() विधि के साथ एक अमूर्त वर्ग रहा है, तो यह एक ही कार्यान्वयन होने के बजाय के बारे में जानता है, जो List<T> में ओवरराइड हो सकता था। मैं यह नहीं कह रहा हूं कि यह हमेशा प्रासंगिक है, न ही IEnumerable<T> एक अमूर्त वर्ग होना चाहिए (निश्चित रूप से नहीं!) - बस इसे एक छोटे से संभावित नुकसान के रूप में इंगित करना चाहिए। यही वह जगह है जहां मौजूदा व्यवहार को विशेषज्ञता देकर बहुरूपता वास्तव में उचित होगी (स्वीकार्य रूप से जिस तरह से परिणाम के बजाय प्रदर्शन को प्रभावित करता है)।

+0

धन्यवाद, जॉन। "ठोस कार्यान्वयन अधिक अनुकूलित संस्करण प्रदान कर सकता है" कंक्रीट संस्करण दोनों दृष्टिकोणों में ऐसा नहीं कर सकता है? "किसी एक्सटेंशन विधि को कभी भी नहीं कहा जाएगा यदि इसमें" http://msdn.microsoft.com/en-us/library/bb383977.aspx जैसा लगता है कि आप ओवरराइड कर सकते हैं कंक्रीट वर्ग में विस्तार विधियों वैसे भी। मुझे लगता है कि अगर ग्राहक इसे आईबीएएस में रखता है तो यह नहीं होगा, लेकिन यह सार वर्ग के दृष्टिकोण के साथ समान नहीं है? – dss539

+3

नहीं, आप वास्तव में उन्हें ओवरराइड नहीं कर सकते हैं। आप एक ही हस्ताक्षर के साथ विधियां प्रदान कर सकते हैं और फिर कॉलर को उनके बारे में * * * कॉल किया जाएगा - लेकिन निष्पादन-समय प्रकार के आधार पर उन्हें * polymorphically * नहीं कहा जाएगा। –

+1

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

1

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

मेरे लिए अमूर्त वर्ग हमेशा इंटरफेस का उपयोग करते हुए घबराहट लगते हैं, मेरे पास ऑब्जेक्ट फैक्ट्री हो सकती है जो एक ऑब्जेक्ट देता है जो मैं पूरा करने की कोशिश कर रहा हूं (चिंताओं को अलग करना)।

बस बनाने कुछ एक इंटरफेस गणित कहा जाता है, को जोड़ने घटाना, विभाजन और गुणा और फिर मैं एक वर्ग IntMAth कहा जाता है कि पूर्णांक गणित के लिए अनुकूलित है कि गणित को लागू करता है, और मुझे एक और वर्ग FloatMath कहा जाता है है है फ़्लोटिंग मैथ के लिए अनुकूलित मैथ लागू करता है, और मेरे पास एक सामान्य गणित है जो मैथ लागू करता है जो बाकी सब कुछ संभालता है।

जब मुझे कुछ फ्लोट जोड़ने की ज़रूरत है तो मैं अपने कारखाने MathFactory.getMath (टाइपोफ (फ्लोट)) को कॉल कर सकता हूं और इसमें कुछ तर्क है कि अगर मैं जिस प्रकार में गुजर रहा हूं वह एक फ्लोट है तो यह फ़्लोटमैथ क्लास लौटाता है ।

इस तरह मेरी कक्षाओं के सभी छोटे और अधिक पोषणीय हैं, कोड है कि कक्षाओं कॉल छोटा होता है, आदि

+1

मुझे लगता है कि कक्षा गणित {} और कक्षा फ़्लोटमैथ: गणित {} यह है कि मैं इसके बारे में कैसे जाऊंगा। एक कारखाने की जरूरत से बचें। – dss539

+1

इस मामले में मैं सहमत हूं, लेकिन यह सिर्फ एक उदाहरण है जो मैं फ्लाई पर आया था। –

23

IMHO, यह गलत सवाल है।

आपको इसके लिए डिज़ाइन किए गए सभी चीज़ों का उपयोग करना चाहिए।

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

  • सार आधार वर्गों का वास्तव में अक्सर "पुन: उपयोग कोड" (वास्तविक विरासत के बजाय) का दुरुपयोग किया जाता है। यह सामान्य रूप से विरासत पर लागू होता है।

सवाल किया जाना चाहिए: "मैं इंटरफेस, विस्तार तरीकों या आधार वर्ग का उपयोग करना चाहिए जब"

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

संपादित करें:

या सवाल किया जाना चाहिए: "मैं पुन: प्रयोज्य कार्यक्षमता है कि एक आधार वर्ग से संबंधित नहीं है कैसे लिख सकता हूँ"

  • एक अंतरफलक है कि कार्यक्षमता
  • एक पुन: प्रयोज्य पुस्तकालय वर्ग है कि कार्यक्षमता
  • एक वर्ग है कि इंटरफ़ेस को लागू करता है और पुन: प्रयोज्य वर्ग के योग के द्वारा कार्यक्षमता पुनः उपयोग कर लेता लिखने को लागू करता है लिखने को उजागर करता है लिखते हैं।

आम तौर पर मैं कहूंगा कि विशेष मामलों या विशेष डिजाइन निर्णयों को छोड़कर, विस्तार तर्क व्यावसायिक तर्क के लिए गलत जगह है।

बेस क्लास केवल दुर्लभ मामलों में सही निर्णय हैं। संदेह में, यह नहीं है। इसमें कोई संदेह नहीं है, आपको इसके बारे में फिर से सोचना चाहिए।

+0

एक्सटेंशन विधियां असली encapsulation नहीं हैं? शायद मैं समझ में नहीं आता, लेकिन यह encapsulation कैसे तोड़ता है? क्या आप कह रहे हैं कि विस्तार विधियां किसी वस्तु को राज्य की जानकारी रखने से रोकती हैं जो निजी है? किसी भी मामले में, इंटरफ़ेस बनाम बेस क्लास का उपयोग करने के बारे में आपकी सलाह Krzyzstof Cwalina की सलाह के विपरीत है। http://en.csharp-online.net/.NET_Type_Design_Guidelines%E2%80%94Choosing_Between_Class_and_Interface। – dss539

+0

'कुछ डिज़ाइन किए गए किसी भी चीज़ का उपयोग करने' के बारे में आपका मुद्दा महत्वपूर्ण है क्योंकि इसे डिज़ाइन किए गए कुछ का उपयोग आमतौर पर पठनीयता को बढ़ाता है। +1 हालांकि, कुछ तरीकों का उपयोग करके डिजाइनरों को समझ में नहीं आया वास्तव में वे जिस तरह से इरादा रखते थे (कुछ मामलों में, सभी नहीं) का उपयोग करने से बेहतर हो सकते हैं। – dss539

+0

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

संबंधित मुद्दे