इंटरफ़ेस क्यों लागू करें स्पष्ट रूप से?


122

तो, स्पष्ट रूप से एक इंटरफ़ेस को लागू करने के लिए एक अच्छा उपयोग मामला क्या है?

क्या यह केवल इतना है कि वर्ग का उपयोग करने वाले लोगों को उन सभी तरीकों / गुणों पर गौर करने की ज़रूरत नहीं है?

जवाबों:


146

यदि आप एक ही विधि और अलग-अलग कार्यान्वयन के साथ दो इंटरफेस लागू करते हैं, तो आपको स्पष्ट रूप से लागू करना होगा।

public interface IDoItFast
{
    void Go();
}
public interface IDoItSlow
{
    void Go();
}
public class JustDoIt : IDoItFast, IDoItSlow
{
    void IDoItFast.Go()
    {
    }

    void IDoItSlow.Go()
    {
    }
}

हाँ, यह एक ऐसा मामला है जिसे EIMI हल करता है। और अन्य बिंदु "माइकल बी" उत्तर द्वारा कवर किए गए हैं।
क्रिप्टेड

10
बहुत बढ़िया उदाहरण है। इंटरफ़ेस / वर्ग के नाम प्यार! :-)
ब्रायन रोजर्स

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

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

@BrianRogers और विधि नाम भी ;-)
SANƒошӽа

66

यह गैर-पसंदीदा सदस्य को छिपाने के लिए उपयोगी है। उदाहरण के लिए, यदि आप दोनों को लागू करते हैं IComparable<T>और IComparableयह आमतौर पर IComparableलोगों को यह आभास नहीं देने के लिए ओवरलोड को छिपाने के लिए अच्छा है कि आप विभिन्न प्रकार की वस्तुओं की तुलना कर सकते हैं। इसी तरह, कुछ इंटरफेस सीएलएस-अनुपालन नहीं हैं, जैसे IConvertible, यदि आप इंटरफ़ेस को स्पष्ट रूप से लागू नहीं करते हैं , तो उन भाषाओं के अंतिम उपयोगकर्ता जिनके लिए सीएलएस अनुपालन की आवश्यकता होती है, वे आपके ऑब्जेक्ट का उपयोग नहीं कर सकते हैं। (अगर बीसीएल कार्यान्वयनकर्ताओं ने आदिम सदस्यों के गैर-परिवर्तनीय सदस्यों को छिपाया नहीं तो यह बहुत विनाशकारी होगा))

एक और दिलचस्प नोट यह है कि आम तौर पर इस तरह के निर्माण का उपयोग करने का मतलब है कि एक इंटरफ़ेस को स्पष्ट रूप से लागू करने वाली संरचना केवल इंटरफ़ेस प्रकार के लिए बॉक्सिंग करके उन्हें आमंत्रित कर सकती है। आप सामान्य बाधाओं का उपयोग करके इसे प्राप्त कर सकते हैं ::

void SomeMethod<T>(T obj) where T:IConvertible

जब आप इसे पास करेंगे तो एक इंट को बॉक्स नहीं करेंगे।


1
आपके पास एक बाधा है। लिपिकीय करने के लिए, ऊपर दिया गया कोड काम करता है। यह इंटरफ़ेस में विधि हस्ताक्षर की प्रारंभिक घोषणा में होना चाहिए। मूल पोस्ट ने इसे निर्दिष्ट नहीं किया था। इसके अलावा, उचित प्रारूप "शून्य SomeMehtod <T> (T obj) है जहां T: IConvertible। ध्यान दें," "और" जहाँ "नहीं होना चाहिए, के बीच एक अतिरिक्त कोलन है। फिर भी, चतुर उपयोग के लिए +1। जेनरिक महंगा मुक्केबाजी से बचने के लिए।
जैक Jannsen

1
हाय माइकल बी। तो क्यों .NET में स्ट्रिंग के कार्यान्वयन में वहाँ IComparable का सार्वजनिक कार्यान्वयन: सार्वजनिक int तुलना (वस्तु मान) {अगर (मूल्य == शून्य) {वापसी 1; } यदि (((मान स्ट्रिंग है)) {नया तर्क फेंकना (पर्यावरण। GetResourceString ("Arg_MustBeString")); } String.Compare (यह, (String) मान, StringComparison.CurrentCulture); } धन्यवाद!
zzfima

stringजेनरिक से पहले चारों ओर था और यह प्रथा प्रचलन में थी। जब .net 2 के आसपास आया stringतो वे सार्वजनिक इंटरफ़ेस को तोड़ना नहीं चाहते थे, इसलिए उन्होंने इसे चारों ओर छोड़ दिया क्योंकि यह जगह में सुरक्षा के साथ था।
माइकल बी

37

एक इंटरफ़ेस को स्पष्ट रूप से लागू करने के लिए कुछ अतिरिक्त कारण:

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

क्लीनर कोड : यदि CloneICloneable से विधि को हटा दिया जाता है, तो एक संकलक त्रुटि होगी , हालाँकि यदि आप इस पद्धति को कार्यान्वित करते हैं तो आप अप्रयुक्त 'अनाथ' सार्वजनिक तरीकों से समाप्त हो सकते हैं

मजबूत टाइपिंग : एक उदाहरण के साथ सुपरकैट की कहानी को स्पष्ट करने के लिए, यह मेरा पसंदीदा नमूना कोड होगा, इसे ICloneableस्पष्ट रूप से लागू करने की अनुमति देता Clone()है जब आप इसे सीधे MyObjectउदाहरण के सदस्य के रूप में कहते हैं :

public class MyObject : ICloneable
{
  public MyObject Clone()
  {
    // my cloning logic;  
  }

  object ICloneable.Clone()
  {
    return this.Clone();
  }
}

उस एक के लिए, मैं चाहूंगा interface ICloneable<out T> { T Clone(); T self {get;} }। ध्यान दें कि ICloneable<T>टी। पर जानबूझकर कोई बाधा नहीं है। जबकि एक वस्तु को आम तौर पर केवल सुरक्षित रूप से क्लोन किया जा सकता है यदि इसका आधार हो सकता है, तो कोई एक आधार वर्ग से प्राप्त करना चाह सकता है जिसे सुरक्षित रूप से उस वर्ग का एक ऑब्जेक्ट क्लोन किया जा सकता है जो नहीं कर सकता है। इसके लिए अनुमति देने के लिए, मैं अनुशंसा करूंगा कि अंतर्निहित वर्ग एक सार्वजनिक क्लोन पद्धति को उजागर करें। इसके बजाय एक protectedक्लोनिंग विधि के साथ अंतर्निहित कक्षाएं हैं और सीलबंद कक्षाएं जो उनसे निकलती हैं और सार्वजनिक क्लोनिंग को उजागर करती हैं।
सुपरकैट

सुनिश्चित करें कि यह अच्छा होगा, सिवाय इसके कि बीसीएल में ICloneable का कोई सहसंयोजक संस्करण नहीं है, इसलिए आपको एक अधिकार बनाना होगा?
विएब तिजमा

सभी 3 उदाहरण असंभावित स्थितियों पर निर्भर करते हैं और सर्वोत्तम प्रथाओं को तोड़ते हैं।
मिकडी

13

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

उदाहरण के लिए, कोई वस्तु कार्यान्वित कर सकती है ICloneable, लेकिन फिर भी उसकी सार्वजनिक रूप से दिखाई देने वाली Cloneविधि अपना ही प्रकार लौटाती है।

इसी तरह, IAutomobileFactoryएक Manufactureविधि हो सकती है जो एक रिटर्न देती है Automobile, लेकिन एक FordExplorerFactory, जो लागू होती है IAutomobileFactory, उसकी Manufactureविधि वापस आ सकती है FordExplorer(जो कि प्राप्त होती है Automobile)। कोड जो जानता है कि यह टाइपकास्ट किए बिना वापस लौटाए गए ऑब्जेक्ट पर -pecific गुणों का FordExplorerFactoryउपयोग कर सकता है , जबकि कोड जो केवल यह जानता था कि यह किसी प्रकार का था बस एक के रूप में अपनी वापसी के साथ सौदा करेगा ।FordExplorerFordExplorerFactoryIAutomobileFactoryAutomobile


2
+1 ... यह स्पष्ट इंटरफ़ेस कार्यान्वयन का मेरा पसंदीदा उपयोग होगा, हालांकि एक छोटा कोड नमूना शायद इस कहानी की तुलना में थोड़ा अधिक स्पष्ट होगा :)
Wiebe Tijsma

7

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

interface Cat
{
    string Name {get;}
}

interface Dog
{
    string Name{get;}
}

public class Animal : Cat, Dog
{
    string Cat.Name
    {
        get
        {
            return "Cat";
        }
    }

    string Dog.Name
    {
        get
        {
            return "Dog";
        }
    }
}
static void Main(string[] args)
{
    Animal animal = new Animal();
    Cat cat = animal; //Note the use of the same instance of Animal. All we are doing is picking which interface implementation we want to use.
    Dog dog = animal;
    Console.WriteLine(cat.Name); //Prints Cat
    Console.WriteLine(dog.Name); //Prints Dog
}

60
सबसे अजीब OO संबंधित उदाहरण मैंने कभी देखा है:public class Animal : Cat, Dog
mbx

38
@ समाधि: यदि पशु ने तोते को भी लागू किया, तो यह एक पोली-मॉर्फिंग जानवर होगा।
RenniePet

3
मुझे एक कार्टून चरित्र याद है जो एक छोर पर एक बिल्ली और दूसरे छोर पर एक कुत्ता था ;-)
जॉर्ज बिरबिलिस

2
80 के दशक में .. "मणिमल" .. में एक टीवी सीरीज़ थी, जहाँ एक आदमी एक .. मेंहदी लगा सकता था
bkwdesign

मोर्चे थोड़े बिल्लियों की तरह दिखते हैं, और निंजा-बिल्ली की तरह भी हैं।
Samis

6

यह एक इंटरफ़ेस को स्पष्ट रूप से लागू करने के लिए सार्वजनिक इंटरफ़ेस क्लीनर रख सकता है, अर्थात आपका Fileवर्ग स्पष्ट रूप से लागू कर सकता है IDisposableऔर एक सार्वजनिक विधि प्रदान Close()कर सकता है जो उपभोक्ता की तुलना में अधिक समझ में आता है Dispose()।

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


मुझे लगता है कि VB के अधिकांश संस्करणों ने केवल स्पष्ट इंटरफ़ेस परिभाषाओं का समर्थन किया है।
गाबे

1
@Gabe - यह VB के लिए उससे अधिक सूक्ष्म है - एक इंटरफ़ेस को लागू करने वाले सदस्यों के नामकरण और पहुंच यह इंगित करने से अलग है कि वे कार्यान्वयन का हिस्सा हैं। इसलिए VB में, और @ Iain के उत्तर (वर्तमान शीर्ष उत्तर) को देखते हुए, आप IDoItFast और IDoItSlow को सार्वजनिक सदस्यों "GoFast" और "GoSlow" के साथ क्रमशः लागू कर सकते हैं।
डेमियन_इन_यूएनबेलिएवर

2
मुझे आपका विशेष उदाहरण (IMHO) पसंद नहीं है, केवल जो चीजें छिपानी चाहिए, Disposeवे हैं जिन्हें कभी भी सफाई की आवश्यकता नहीं होगी); एक बेहतर उदाहरण एक अपरिवर्तनीय संग्रह के कार्यान्वयन की तरह होगा IList<T>.Add
सुपरकैट

5

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


ठीक है, यह बताता है कि क्यों परियोजना एक अंतर्निहित कार्यान्वयन के साथ संकलन नहीं करेगी।
पलोया

4

स्पष्ट कार्यान्वयन के लिए एक और कारण के लिए है रख-रखाव

जब एक वर्ग "व्यस्त" हो जाता है - हाँ ऐसा होता है, हम सभी के पास अन्य टीम के सदस्यों के कोड को फिर से लाने का लक्जरी नहीं है - तो एक स्पष्ट कार्यान्वयन होने से यह स्पष्ट होता है कि एक इंटरफ़ेस अनुबंध को संतुष्ट करने के लिए एक विधि है।

तो यह कोड की "पठनीयता" में सुधार करता है।


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

1

द्वारा एक अलग उदाहरण दिया जाता है System.Collections.Immutable , जिसमें लेखकों ने इंटरफ़ेस के उन हिस्सों को दूर करते हुए संग्रह प्रकारों के लिए एक परिचित एपीआई को संरक्षित करने के लिए तकनीक का उपयोग करने का विकल्प चुना जो अपने नए प्रकारों के लिए कोई अर्थ नहीं रखते हैं।

वस्तुतः, ImmutableList<T>औजार IList<T>और इस प्रकार ICollection<T>( क्रम में अनुमति देने के लिए ImmutableList<T>अभी तक, विरासत कोड के साथ और अधिक आसानी से इस्तेमाल किया जा करने के लिए) void ICollection<T>.Add(T item)एक के लिए कोई मतलब नहीं है ImmutableList<T>अपरिवर्तनीय सूची में कोई तत्व जोड़ मौजूदा सूची में परिवर्तन नहीं करना चाहिए, के बाद से: ImmutableList<T>भी व्युत्पन्न से IImmutableList<T>जिसका IImmutableList<T> Add(T item)के लिए इस्तेमाल किया जा सकता अपरिवर्तनीय सूचियाँ।

इस प्रकार के मामले में Add, कार्यान्वयन ImmutableList<T>इस प्रकार दिख रहा है:

public ImmutableList<T> Add(T item)
{
    // Create a new list with the added item
}

IImmutableList<T> IImmutableList<T>.Add(T value) => this.Add(value);

void ICollection<T>.Add(T item) => throw new NotSupportedException();

int IList.Add(object value) => throw new NotSupportedException();

0

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

interface Iphone{

   void Money();

}

interface Ipen{

   void Price();
}


class Demo : Iphone, Ipen{

  void Iphone.Money(){    //it is private you can't give public               

      Console.WriteLine("You have no money");
  }

  void Ipen.Price(){    //it is private you can't give public

      Console.WriteLine("You have to paid 3$");
  }

}


// So you have to cast to call the method


    class Program
    {
        static void Main(string[] args)
        {
            Demo d = new Demo();

            Iphone i1 = (Iphone)d;

            i1.Money();

            ((Ipen)i1).Price();

            Console.ReadKey();
        }
    }

  // You can't call methods by direct class object

1
"सभी विधियाँ स्वचालित रूप से निजी हैं" - यह तकनीकी रूप से सही नहीं है, b / c यदि वे वास्तव में निजी थे तो वे कॉल करने योग्य नहीं होंगे, कास्टिंग या नहीं।
सैम

0

यह है कि हम स्पष्ट इंटरफ़ेस कैसे बना सकते हैं: यदि हमारे पास 2 इंटरफ़ेस हैं और दोनों इंटरफ़ेस में एक ही विधि है और एक एकल वर्ग इन 2 इंटरफेस को इनहेरिट करता है, तो जब हम एक इंटरफ़ेस विधि को कॉल करते हैं तो कंपाइलर भ्रमित हो जाता है कि किस विधि को बुलाया जाए, इसलिए हम कर सकते हैं स्पष्ट इंटरफ़ेस का उपयोग करके इस समस्या का प्रबंधन करें। यहाँ एक उदाहरण मैंने नीचे दिया है।

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace oops3
{
    interface I5
    {
        void getdata();    
    }
    interface I6
    {
        void getdata();    
    }

    class MyClass:I5,I6
    {
        void I5.getdata()
        {
           Console.WriteLine("I5 getdata called");
        }
        void I6.getdata()
        {
            Console.WriteLine("I6 getdata called");
        }
        static void Main(string[] args)
        {
            MyClass obj = new MyClass();
            ((I5)obj).getdata();                     

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