क्या मुझे एक इंटरफ़ेस का उपयोग करने की आवश्यकता है जब केवल एक वर्ग कभी इसे लागू करेगा?


242

क्या इंटरफ़ेस का पूरा बिंदु नहीं है कि कई वर्ग नियमों और कार्यान्वयनों के एक समूह का पालन करते हैं?



16
या इसे एकतरफा करना आसान है।

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

2
इस सवाल पर रेडिट चर्चा
यानिस

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

जवाबों:


204

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

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


84
+1 परीक्षण का मतलब है कि आपके पास लगभग हमेशा दो कार्यान्वयन हैं वैसे भी
jk।

24
Yagni की वजह से @YannisRizos आपके बाद के बिंदु से असहमत है। सार्वजनिक तरीकों से एक वर्ग को क्रैंक करना इस तथ्य के बाद तुच्छ है, क्योंकि उपभोग की कक्षाओं में IFoo के साथ CFoo की जगह है। जरूरत से पहले इसे लिखने का कोई मतलब नहीं है।
डैन नीली

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

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

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

144

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

यहाँ एक बहुत ही उपयोगी नियम है:

  • यदि आप कक्षा को Fooसीधे कक्षा के संदर्भ में बनाते हैं BarImpl, तो आप Fooहर बार बदलने के लिए खुद को बदलने के लिए दृढ़ता से प्रतिबद्ध हैं BarImpl। आप मूल रूप से उन्हें कोड की एक इकाई के रूप में मान रहे हैं जो दो वर्गों में विभाजित है।
  • यदि आप इंटरफ़ेसFoo को संदर्भित करते हैं, तो जब आप बदलते हैं तो परिवर्तनों से बचने के लिए आप स्वयं को कमिट कर रहे हैं । BarFooBarImpl

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

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


14
मैं इसे एक से अधिक बार बढ़ाऊंगा, अगर मैं कर सका। इस सवाल का सबसे अच्छा जवाब IMO।
फैबियो फ्रैकासी

4
यदि वर्ग केवल एक भूमिका निभा रहा है (अर्थात इसके लिए केवल एक इंटरफ़ेस परिभाषित होगा), तो सार्वजनिक / निजी तरीकों के माध्यम से ऐसा क्यों न करें?
मैथ्यू फिनेले

4
1. कोड संगठन; अपनी स्वयं की फ़ाइल में इंटरफ़ेस जिसमें केवल हस्ताक्षर और प्रलेखन टिप्पणियां हैं, कोड को साफ रखने में मदद करता है। 2. फ्रेमवर्क जो आपको उन तरीकों को उजागर करने के लिए मजबूर करते हैं जिन्हें आप निजी रखेंगे (जैसे, कंटेनर जो सार्वजनिक बसने वालों के माध्यम से निर्भरता को इंजेक्ट करते हैं)। 3. अलग संकलन, जैसा कि पहले ही उल्लेख किया गया है; यदि वर्ग Fooइंटरफ़ेस पर निर्भर करता है, Barतो BarImplबिना रीकॉम्पेल किए संशोधित किया जा सकता है Foo। 4. सार्वजनिक / निजी ऑफ़र की तुलना में महीन-महीन एक्सेस कंट्रोल (एक ही क्लास को दो क्लाइंट्स को अलग-अलग इंटरफेस के माध्यम से उजागर करना)।
sacundim

3
और आखिरी एक: 5. ग्राहकों (आदर्श रूप से) को यह ध्यान नहीं देना चाहिए कि मेरे मॉड्यूल में कितने वर्ग हैं या कितने हैं, या यहां तक ​​कि बताने में सक्षम हैं। उन्हें जो कुछ भी देखना चाहिए , वह गेंद को लुढ़काने के लिए कुछ प्रकारों और कुछ कारखानों या फेक का सेट है । यानी, मैं अक्सर यह मानकर देखता हूं कि आपकी लाइब्रेरी में कौन सी कक्षाएं हैं।
sacundim

3
@ सैकुंडिम बारिम्पल If you make class Foo refer directly to class BarImpl, you're strongly committing yourself to change Foo every time you change BarImplको बदलने पर, फू में किन बदलावों से बचा जा सकता है interface Bar? जब तक एक विधि के हस्ताक्षर और कार्यक्षमता BarImpl में नहीं बदलती, तब तक Foo को इंटरफ़ेस (इंटरफ़ेस का पूरा उद्देश्य) के बिना भी बदलाव की आवश्यकता नहीं होगी। मैं उस परिदृश्य के बारे में बात कर रहा हूँ जहाँ केवल एक वर्ग यानी BarImplबार को लागू करता है। मल्टी क्लास परिदृश्य के लिए, मैं डिपेंडेंसी इनवर्जन प्रिंसिपल को समझता हूं और यह कैसे इंटरफ़ेस सहायक है।
शिशिर गुप्ता

101

किसी व्यवहार को परिभाषित करने के लिए इंटरफेस को नामित किया गया है, अर्थात फ़ंक्शन / विधियों के प्रोटोटाइप का एक सेट। इंटरफ़ेस को लागू करने वाले प्रकार उस व्यवहार को लागू करेंगे, इसलिए जब आप इस प्रकार के साथ व्यवहार करते हैं तो आप जानते हैं (आंशिक रूप से) कि यह कैसा व्यवहार है।

एक इंटरफ़ेस को परिभाषित करने की आवश्यकता नहीं है यदि आप जानते हैं कि इसके द्वारा परिभाषित व्यवहार केवल एक बार उपयोग किया जाएगा। KISS (यह आसान बेवकूफ रखने के लिए,)


हमेशा नहीं, आप एक वर्ग में इंटरफ़ेस का उपयोग कर सकते हैं यदि वह वर्ग एक सामान्य वर्ग है।
जॉन यशायाह कारमोना

इसके अलावा, आप अंतरापृष्ठ में आसानी से जरूरत के हिसाब से एक इंटरफ़ेस निकाल सकते हैं, "एक्सट्रैक्ट इंटरफेस" रिफैक्टिंग।
हंस-पीटर स्टॉर

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

63

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

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

लेकिन रुकिए, और भी है। अधिक परिदृश्य हैं जहां निहित इंटरफ़ेस कार्यान्वयन हैं। उदाहरण के लिए .NET के WCF संचार स्टैक का उपयोग करना, एक दूरस्थ सेवा के लिए एक प्रॉक्सी बनाता है, जो फिर से, इंटरफ़ेस को लागू करता है।

एक स्वच्छ कोड वातावरण में, मैं यहां बाकी जवाबों से सहमत हूं। हालाँकि, किसी भी चौखटे, पैटर्न या निर्भरता पर ध्यान दें जो आपके लिए इंटरफेस का उपयोग कर सकती है।


2
+1: मुझे यकीन नहीं है कि YAGNI यहां लागू होता है, इंटरफ़ेस के रूप में और फ्रेमवर्क (जैसे JMock, आदि) का उपयोग करके जो इंटरफेस का उपयोग करते हैं, वास्तव में आप समय बचा सकते हैं।
डेको

4
@ डेको: अच्छे मॉकिंग फ्रेमवर्क (उनके बीच JMock) को भी इंटरफेस की आवश्यकता नहीं है।
माइकल Borgwardt

12
पूरी तरह से अपने नकली ढांचे की एक सीमा के कारण एक इंटरफ़ेस बनाना मेरे लिए एक भयानक कारण की तरह लगता है। उदाहरण के लिए, EasyMock क्लास का उपयोग करना एक इंटरफ़ेस के समान आसान है।
Alb

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

1
आप उत्पादन में परीक्षण बंद नहीं कर रहे हैं। एक वर्ग के लिए एक मॉक की आवश्यकता क्यों है, जिसे इंटरफ़ेस की आवश्यकता नहीं है, इंटरफ़ेस की आवश्यकता है?
एरिक रेपेन

32

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

सब कुछ के लिए फू / FooImpl बनाने के लिए एक वास्तविक लागत है। IDE मुक्त करने के लिए इंटरफ़ेस / कार्यान्वयन बना सकता है, लेकिन जब आप कोड नेविगेट कर रहे हों, तो आपके पास foo.doSomething()इंटरफ़ेस हस्ताक्षर लेने के लिए F3 / F12 से अतिरिक्त संज्ञानात्मक भार है, न कि वास्तविक कार्यान्वयन जिसे आप चाहते हैं। साथ ही आपके पास हर चीज के लिए एक के बजाय दो फाइलों का समूह है।

इसलिए आपको इसे तभी करना चाहिए जब आपको वास्तव में किसी चीज की जरूरत हो।

अब प्रतिवादों को संबोधित करते हुए:

मुझे डिपेंडेंसी इंजेक्शन फ्रेमवर्क के लिए इंटरफेस चाहिए

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

मुझे यूनिट टेस्टिंग के लिए मोक्स चाहिए

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

लेकिन अगर आप Moq, EasyMock, या Mockito जैसे मॉकिंग फ्रेमवर्क का उपयोग कर रहे हैं, तो आप कक्षाएं मॉक कर सकते हैं और आप इंटरफेस नहीं बनाते हैं। यह foo.method = mockImplementationएक गतिशील भाषा में स्थापित करने के लिए अनुरूप है जहां तरीकों को सौंपा जा सकता है।

हमें डिपेंडेंसी इनवर्जन सिद्धांत (DIP) का पालन करने के लिए इंटरफेस की आवश्यकता है

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

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

यह अंकल बॉब मार्टिन के मॉकिंग के बारे में एक कोरोलरी है - आपको केवल प्रमुख वास्तुशिल्प सीमाओं पर मज़ाक करना चाहिए। एक वेबपेज में HTTP और DB एक्सेस प्रमुख सीमाएँ हैं। बीच में सभी वर्ग / विधि कॉल नहीं हैं। वही डीआईपी के लिए जाता है।

यह सभी देखें:


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

4
मैं मॉकिंग क्लासेस के साथ समस्याओं में नहीं गया हूं, लेकिन मुझे उम्मीद के मुताबिक काम नहीं कर रहे आईडीई नेविगेशन से निराशा हुई है। एक वास्तविक समस्या का समाधान एक काल्पनिक धड़कता है।
wrschneider

1
@ जूल्स आप जावा में इसके निर्माणकर्ता सहित एक ठोस वर्ग का मजाक उड़ा सकते हैं।
15

1
@assylias आप कंस्ट्रक्टर को चलाने से कैसे रोक सकते हैं?
जूल्स

2
@ जूल्स यह आपके मॉकिंग फ्रेमवर्क पर निर्भर करता है - उदाहरण के लिए जॉकिट के साथ, आप बस लिख सकते हैं new Mockup<YourClass>() {}और इसके कंस्ट्रक्टरों सहित पूरे वर्ग का मज़ाक उड़ाया जाता है, चाहे वह एक इंटरफ़ेस, अमूर्त वर्ग या कंक्रीट क्लास हो। यदि आप ऐसा करना चाहते हैं तो आप कंस्ट्रक्टर के व्यवहार को "ओवरराइड" भी कर सकते हैं। मुझे लगता है कि मॉकिटो या पॉवर्मॉक में समान तरीके हैं।
assylias

22

ऐसा लगता है कि बाड़ के दोनों किनारों पर दिए गए उत्तर इस में सम्‍मिलित किए जा सकते हैं:

अच्छी तरह से डिजाइन करें, और जहां इंटरफेस की आवश्यकता होती है, वहां इंटरफेस लगाएं।

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

उदाहरण के लिए (बुरी तरह से वंचित) उदाहरण के लिए, मान लें कि आप एक Carवर्ग बना रहे हैं । आपकी कक्षा में, आपको निश्चित रूप से UI परत की आवश्यकता होगी। इस विशिष्ट उदाहरण में, यह एक का रूप ले लेता IginitionSwitch, SteeringWheel, GearShift, GasPedal, और BrakePedal। चूंकि इस कार में AutomaticTransmissionए है, इसलिए आपको इसकी आवश्यकता नहीं है ClutchPedal। (और चूंकि यह एक भयानक कार है, कोई ए / सी, रेडियो या सीट नहीं है। वास्तव में, फ़्लोरबोर्ड भी गायब हैं - आपको बस उस स्टीयरिंग व्हील पर लटकना होगा और सर्वश्रेष्ठ की उम्मीद करनी होगी!)

तो इनमें से किस वर्ग को एक इंटरफ़ेस की आवश्यकता है? जवाब उन सभी में से हो सकता है, या उनमें से कोई भी - आपके डिजाइन पर निर्भर करता है।

आपके पास एक इंटरफ़ेस हो सकता है जो इस तरह दिखे:

Interface ICabin
    Event IgnitionSwitchTurnedOn()
    Event IgnitionSwitchTurnedOff()
    Event BrakePedalPositionChanged(int percent)
    Event GasPedalPositionChanged(int percent)
    Event GearShiftGearChanged(int gearNum)
    Event SteeringWheelTurned(float degree)
End Interface

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

यह वही कारण है कि आप निजी सदस्यों को इकाई-परीक्षण नहीं करते हैं - वे केवल सार्वजनिक एपीआई का समर्थन करने के लिए मौजूद हैं , और इस प्रकार उनके व्यवहार को एपीआई का परीक्षण करके परीक्षण किया जाना चाहिए।

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


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

बार-बार (इस उत्तर सहित) आप 'डोमेन', 'निर्भरता' (अक्सर 'इंजेक्शन' के साथ युग्मित) जैसी चीजों को पढ़ेंगे, जो कि आपके लिए एक ऐसी चीज का मतलब नहीं है जब आप कार्यक्रम शुरू कर रहे हैं (उन्हें यकीन है कि नहीं किया था मेरे लिए कुछ भी मतलब है)। डोमेन के लिए, इसका मतलब है कि यह कैसा लगता है:

वह क्षेत्र जिस पर प्रभुत्व या अधिकार छेड़ा हुआ है; एक संप्रभु या सामान्य राष्ट्र या इस तरह की संपत्ति। इसके अलावा आलंकारिक रूप से उपयोग किया जाता है। [वर्डनेट सेंस 2] [१ ९ १३ वेबस्टर]

मेरे उदाहरण के संदर्भ में - आइए विचार करें IgnitionSwitch। मीटस्पेस कार में, इग्निशन स्विच के लिए जिम्मेदार है:

  1. उपयोगकर्ता को पहचानना (पहचानना नहीं) (उन्हें सही कुंजी की आवश्यकता है)
  2. स्टार्टर को करंट प्रदान करना ताकि यह वास्तव में कार को शुरू कर सके
  3. इग्निशन सिस्टम को वर्तमान प्रदान करना ताकि यह काम करना जारी रख सके
  4. चालू बंद तो कार बंद हो जाएगा।
  5. आप इसे कैसे देखते हैं, इसके आधार पर, अधिकांश (सभी?) नई कारों में एक स्विच होता है जो कुंजी को इग्निशन से हटाए जाने से रोकता है जबकि ट्रांसमिशन पार्क से बाहर है, इसलिए यह उसके डोमेन का हिस्सा हो सकता है। (वास्तव में इसका मतलब है कि मुझे अपने सिस्टम पर पुनर्विचार और पुनर्निर्देशित करने की आवश्यकता है ...)

उन गुणों के डोमेन को IgnitionSwitchया दूसरे शब्दों में, इसके बारे में क्या पता है और इसके लिए जिम्मेदार है।

IgnitionSwitchलिए ज़िम्मेदार नहीं है GasPedal। इग्निशन स्विच पूरी तरह से गैस पेडल से अनभिज्ञ है। वे दोनों एक दूसरे से पूरी तरह से स्वतंत्र हैं (हालांकि दोनों के बिना एक कार काफी बेकार होगी!)।

जैसा कि मैंने मूल रूप से कहा है, यह आपके डिजाइन पर निर्भर करता है। आप डिजाइन कर सकते हैं IgnitionSwitchजिसमें दो मान थे: ऑन ( True) और ऑफ ( False)। या आप इसे इसके लिए प्रदान की गई कुंजी, और अन्य कार्यों के होस्ट को प्रमाणित करने के लिए डिज़ाइन कर सकते हैं। एक डेवलपर होने का कठिन हिस्सा यह तय कर रहा है कि रेत में लाइनों को कैसे खींचना है - और ईमानदारी से अधिकांश समय यह पूरी तरह से सापेक्ष है। रेत में वे रेखाएँ महत्वपूर्ण हैं, हालांकि - यही वह जगह है जहाँ आपका एपीआई है, और इस प्रकार जहाँ आपका इंटरफेस होना चाहिए।


क्या आप इस बात पर अधिक विस्तार से बता सकते हैं कि आपके पास "इसका अपना डोमेन है" क्या है?
लामिन सनेह

1
@ लमिनसनेह, विस्तृत। क्या उससे मदद हुई?
वेन वर्नर

8

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


8

से MSDN :

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

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

इंटरफेस उन स्थितियों में बेहतर होते हैं जिनमें आपको आधार वर्ग से कार्यान्वयन की आवश्यकता नहीं होती है।

इंटरफेस उन मामलों में उपयोगी होते हैं जहां आप क्लास इनहेरिटेंस का उपयोग नहीं कर सकते हैं। उदाहरण के लिए, संरचनाएं कक्षाओं से विरासत में नहीं मिल सकती हैं, लेकिन वे इंटरफेस को लागू कर सकते हैं।

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


5

प्रश्न का उत्तर देने के लिए: इसके अलावा भी बहुत कुछ है।

एक इंटरफ़ेस का एक महत्वपूर्ण पहलू इरादे है।

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

पूछने का पहला सवाल यह है कि उस चीज़ या प्रक्रिया की प्रकृति क्या है जिसे आप प्रतिनिधित्व करने की कोशिश कर रहे हैं। फिर उस प्रकृति को एक निश्चित तरीके से लागू करने के व्यावहारिक कारणों का पालन करें।


5

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

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

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

भले ही मॉड्यूल बी के पास इसके इंटरफ़ेस का केवल एक कार्यान्वयन है, इंटरफ़ेस अभी भी आवश्यक है।

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


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

4

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

क्लास एक अन्य इंटरफ़ेस को लागू करता है जिसे मैं
एडाप्टर क्लास के साथ हैप्पन को अक्सर उजागर नहीं करना चाहता हूं जो कि थर्ड पार्टी कोड को ब्रिज करता है।

interface NameChangeListener { // Implemented by a lot of people
    void nameChanged(String name); 
} 

interface NameChangeCount { // Only implemented by my class
    int getCount();
}

class NameChangeCounter implements NameChangeListener, NameChangeCount {
    ...
}

class SomeUserInterface {
    private NameChangeCount currentCount; // Will never know that you can change the counter
}

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

interface SomeRepository { // Guarantee that the external library details won't leak trough
    ...
}

class OracleSomeRepository implements SomeRepository { 
    ... // Oracle prefix allow us to quickly know what is going on in this class
}

क्रॉस लेयर कम्युनिकेशन
भले ही केवल एक यूआई वर्ग कभी भी डोमेन वर्ग में से एक को लागू करता है, यह उन परत के बीच बेहतर पृथक्करण की अनुमति देता है और सबसे महत्वपूर्ण बात यह चक्रीय निर्भरता से बचता है।

package project.domain;

interface UserRequestSource {
    public UserRequest getLastRequest();
}

class UserBehaviorAnalyser {
    private UserRequestSource requestSource;
}

package project.ui;

class OrderCompleteDialog extends SomeUIClass implements project.domain.UserRequestSource {
    // UI concern, no need for my domain object to know about this method.
    public void displayLabelInErrorMode(); 

    // They most certainly need to know about *that* though
    public UserRequest getLastRequest();
}

केवल विधि का सबसेट सबसे अधिक वस्तु के लिए उपलब्ध होना चाहिए
ज्यादातर तब होता है जब मेरे पास ठोस वर्ग पर कुछ कॉन्फ़िगरेशन विधि होती है

interface Sender {
    void sendMessage(Message message)
}

class PacketSender implements Sender {
    void sendMessage(Message message);
    void setPacketSize(int sizeInByte);
}

class Throttler { // This class need to have full access to the object
    private PacketSender sender;

    public useLowNetworkUsageMode() {
        sender.setPacketSize(LOW_PACKET_SIZE);
        sender.sendMessage(new NotifyLowNetworkUsageMessage());

        ... // Other details
    }
}

class MailOrder { // Not this one though
    private Sender sender;
}

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


2

इंटरफेस वास्तव में महत्वपूर्ण हैं लेकिन आपके पास उनकी संख्या को नियंत्रित करने का प्रयास करें।

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

http://ayende.com/blog/153889/limit-your-abstractions-analyzing-a-ddd-application

यह एक पूरी श्रृंखला की उनकी पहली पोस्ट है इसलिए पढ़ते रहें!



बहुत अधिक लगता है जैसे मुझे लैसन-कोड या बाकलावा-कोड - बहुत सी परतों वाला कोड। ;-)
dodgy_coder

2

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


2

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


1

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

मूल्य वस्तुओं की तरह सरल वस्तुओं में कई कार्यान्वयन नहीं होते हैं। उन्हें या तो मज़ाक करने की ज़रूरत नहीं है। कार्यान्वयन का परीक्षण स्वयं किया जा सकता है और जब अन्य वर्गों का परीक्षण किया जाता है जो उन पर निर्भर करते हैं, तो वास्तविक मूल्य वस्तु का उपयोग किया जा सकता है।

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

इसलिए मैं केवल उच्च स्तर की कक्षाओं के लिए इंटरफेस को परिभाषित करता हूं, जहां आप कार्यान्वयन से एक अमूर्त चाहते हैं।

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

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