प्रतिनिधि बनाम अंतर-कोई और स्पष्टीकरण उपलब्ध है?


23

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

एक प्रतिनिधि का उपयोग करें जब:

  • एक ईवेंटिंग डिज़ाइन पैटर्न का उपयोग किया जाता है।
  • एक स्थिर विधि को एनकैप्सुलेट करना वांछनीय है।
  • आसान रचना वांछित है।
  • एक वर्ग को विधि के एक से अधिक कार्यान्वयन की आवश्यकता हो सकती है।

जब एक इंटरफ़ेस का उपयोग करें:

  • संबंधित तरीकों का एक समूह है जिसे कहा जा सकता है।
  • एक वर्ग को केवल विधि के एक कार्यान्वयन की आवश्यकता है।

मेरे प्रश्न हैं,

  1. एक इवेंटिंग डिज़ाइन पैटर्न से उनका क्या मतलब है?
  2. यदि प्रतिनिधि का उपयोग किया जाता है तो रचना कैसे आसान हो जाती है?
  3. यदि संबंधित तरीकों का एक समूह है जिसे कॉल किया जा सकता है, तो इंटरफ़ेस का उपयोग करें-इसका क्या लाभ है?
  4. यदि किसी वर्ग को केवल पद्धति के एक कार्यान्वयन की आवश्यकता है, तो इंटरफ़ेस का उपयोग कैसे करें-यह लाभों के संदर्भ में उचित है?

जवाबों:


11

एक इवेंटिंग डिज़ाइन पैटर्न से उनका क्या मतलब है?

वे संभवतः पर्यवेक्षक पैटर्न के कार्यान्वयन का उल्लेख करते हैं जो C # में एक मुख्य भाषा निर्माण है, जिसे ' घटनाओं ' के रूप में उजागर किया गया है । घटनाओं को सुनना एक प्रतिनिधि को हुक करके संभव है। जैसा कि यम मारकोविच ने बताया, EventHandlerघटनाओं के लिए पारंपरिक आधार प्रतिनिधि प्रकार है, लेकिन किसी भी प्रतिनिधि प्रकार का उपयोग किया जा सकता है।

यदि प्रतिनिधि का उपयोग किया जाता है तो रचना कैसे आसान हो जाती है?

यह शायद सिर्फ लचीलेपन प्रतिनिधियों की पेशकश को संदर्भित करता है। आप आसानी से कुछ व्यवहार की रचना कर सकते हैं। लंबोदर की मदद से , ऐसा करने के लिए वाक्यविन्यास भी बहुत संक्षिप्त है। निम्नलिखित उदाहरण पर विचार करें।

class Bunny
{
    Func<bool> _canHop;

    public Bunny( Func<bool> canHop )
    {
        _canHop = canHop;
    }

    public void Hop()
    {
        if ( _canHop() )  Console.WriteLine( "Hop!" );
    }
}

Bunny captiveBunny = new Bunny( () => IsBunnyReleased );
Bunny lazyBunny = new Bunny( () => !IsLazyDay );
Bunny captiveLazyBunny = new Bunny( () => IsBunnyReleased && !IsLazyDay );

इंटरफेस के साथ कुछ ऐसा ही करने के लिए आपको या तो रणनीति पैटर्न का उपयोग करना होगा या ( Bunnyविशिष्ट ) आधार वर्ग का उपयोग करना होगा जहां से आप अधिक विशिष्ट बन्नी का विस्तार करते हैं।

यदि संबंधित तरीकों का एक समूह है जिसे कॉल किया जा सकता है, तो इंटरफ़ेस का उपयोग करें-इसका क्या लाभ है?

फिर, मैं यह प्रदर्शित करने के लिए बन्नी का उपयोग करूँगा कि यह कैसे आसान होगा।

interface IAnimal
{
    void Jump();
    void Eat();
    void Poo();
}

class Bunny : IAnimal { ... }
class Chick : IAnimal { ... }

// Using the interface.
IAnimal bunny = new Bunny();
bunny.Jump();  bunny.Eat();  bunny.Poo();
IAnimal chick = new Chick();
chick.Jump();  chick.Eat();  chick.Poo();

// Without the interface.
Action bunnyJump = () => bunny.Jump();
Action bunnyEat = () => bunny.Eat();
Action bunnyPoo = () => bunny.Poo();
bunnyJump(); bunnyEat(); bunnyPoo();
Action chickJump = () => chick.Jump();
Action chickEat = () => chick.Eat();
...

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

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

निष्कर्ष

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

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

ऐसे परिदृश्य के उदाहरण के रूप में (अधिकतम लचीलापन, स्रोतों को संशोधित करने की आवश्यकता नहीं है), बस दो प्रतिनिधियों को पारित करके किसी भी संभावित संग्रह के लिए द्विआधारी खोज एल्गोरिथ्म के इस कार्यान्वयन पर विचार करें


12

एक प्रतिनिधि एक एकल विधि हस्ताक्षर के लिए एक इंटरफेस की तरह है, जिसे नियमित इंटरफ़ेस की तरह स्पष्ट रूप से लागू नहीं किया जाना है। आप इसे रन पर बना सकते हैं।

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

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

आपके सवालों के लिए:

1 और 2)

यदि आप जावा में एक ईवेंट सिस्टम बनाना चाहते हैं, तो आप आमतौर पर ईवेंट को प्रचारित करने के लिए एक इंटरफ़ेस का उपयोग करते हैं, जैसे कुछ:

interface KeyboardListener
{
    void KeyDown(int key);
    void KeyUp(int key)
    void KeyPress(int key);
    .... and so on
}

इसका मतलब है कि आपकी कक्षा को इन सभी तरीकों को स्पष्ट रूप से लागू करना होगा, और उन सभी के लिए स्टब्स प्रदान करना होगा, भले ही आप बस लागू करना चाहते हों KeyPress(int key)

C # में, इन घटनाओं को प्रतिनिधि की सूचियों के रूप में दर्शाया जाएगा, जो कि "#" कीवर्ड द्वारा c # में छिपा है, प्रत्येक एक विलक्षण घटना के लिए। इसका मतलब है कि आप अपनी कक्षा को सार्वजनिक "की" विधियों आदि से बोझिल किए बिना आसानी से सदस्यता ले सकते हैं।

अंक 3-5 के लिए +1।

इसके अतिरिक्त:

जब आप एक "मानचित्र" फ़ंक्शन उदाहरण के लिए आपूर्ति करना चाहते हैं, तो प्रतिनिधि बहुत उपयोगी होते हैं, जो एक सूची लेता है और प्रत्येक तत्व को एक नई सूची में रखता है, जिसमें समान तत्वों की मात्रा होती है, लेकिन किसी तरह अलग होती है। अनिवार्य रूप से, IEnumerable.Select (...)।

IEnumerable.Select एक लेता है Func<TSource, TDest>, जो एक प्रतिनिधि है जो एक फ़ंक्शन को लपेटता है जो TSource तत्व लेता है और उस तत्व को TDest तत्व में बदल देता है।

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

बेशक, आप अनाम कक्षाओं का उपयोग कर सकते हैं जो एक समान अवधारणा (जावा में) हैं।


वास्तव में उनके सवालों के जवाब देने के लिए अपने पोस्ट को संपादित करने पर विचार करें
यम मार्कोविच

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

सहज रूप में। मेरे लिए भी होता है, और इसका मूल्य प्रति se होता है। इसलिए मैंने केवल इसका सुझाव दिया , लेकिन इसके बारे में कोई शिकायत नहीं की। :)
यम मार्कोविच 14

3

1) इवेंट पैटर्न, अच्छी तरह से क्लासिक ऑब्जर्वर पैटर्न है, यहां एक अच्छा माइक्रोसॉफ्ट लिंक माइक्रोसॉफ्ट टॉक ऑब्जर्वर है

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

ऊपर दिए गए लिंक प्रतिनिधियों के बारे में बात करते हैं और रचना के लिए अच्छे क्यों हैं, ताकि आपकी क्वेरी में मदद मिल सके।


3

सबसे पहले, अच्छा सवाल। मैं नेत्रहीन रूप से "सर्वोत्तम प्रथाओं" को स्वीकार करने के बजाय उपयोगिता पर आपका ध्यान केंद्रित करता हूं। उसके लिए +1।

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

मैं आपके सवालों का जवाब देने के लिए इस बिंदु पर पहुंचूंगा।

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

एक इवेंटिंग डिज़ाइन पैटर्न से उनका क्या मतलब है?

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

जब आप किसी घटना को परिभाषित करते हैं, जैसे कि इस उदाहरण में:

class MyClass {
  // Note: EventHandler is just a multicast delegate,
  // that returns void and accepts (object sender, EventArgs e)!
  public event EventHandler MyEvent;

  public void DoSomethingThatTriggersMyEvent() {
    // ... some code
    var handler = MyEvent;
    if (handler != null)
      handler(this, EventArgs.Empty);
    // ... some other code
  }
}

संकलक वास्तव में इसे निम्नलिखित कोड में बदल देता है:

class MyClass {
  private EventHandler MyEvent = null;

  public void add_MyEvent(EventHandler value) {
    MyEvent += value;
  }

  public void remove_MyEvent(EventHandler value) {
    MyEvent -= value;
  }

  public void DoSomethingThatTriggersMyEvent() {
    // ... some code
    var handler = MyEvent;
    if (handler != null)
      handler(this, EventArgs.Empty);
    // ... some other code
  }
}

आप तब किसी ईवेंट को सब्सक्राइब करें

MyClass instance = new MyClass();
instance.MyEvent += SomeMethodInMyClass;

जिसका संकलन किया जाता है

MyClass instance = new MyClass();
instance.add_MyEvent(new EventHandler(SomeMethodInMyClass));

तो यह C # (या .NET सामान्य रूप से) में इवेंट कर रहा है।

यदि प्रतिनिधि का उपयोग किया जाता है तो रचना कैसे आसान हो जाती है?

यह आसानी से प्रदर्शित किया जा सकता है:

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

interface RequiredMethods {
  void DoX();
  int DoY();
};

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

sealed class RequiredMethods {
  public Action DoX;
  public Func<int> DoY();
}

इस तरह से कॉल करने वालों को केवल आवश्यक प्रक्रिया का एक उदाहरण बनाना होता है और प्रतिनिधियों को रनटाइम पर तरीकों को बाँधना होता है। यह है आम तौर पर आसान।

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

संबंधित विधियों का एक समूह होने पर इंटरफेस का उपयोग करने के लाभ

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

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

जैसा कि पहले उल्लेख किया गया है, संकलन समय में लागू किए जाते हैं - जिसका अर्थ है कि वे एक प्रतिनिधि को आमंत्रित करने की तुलना में अधिक कुशल हैं (जो कि प्रति स्तर अप्रत्यक्ष स्तर है)।

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

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

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

मुझे आशा है कि आपके प्रश्नों का उत्तर आपकी संतुष्टि के लिए दिया गया है। और फिर, सवाल के लिए यश।


2

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

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

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

संबंधित तरीकों के लिए एक इंटरफेस का लाभ यह है कि विधियां लागू करने वाले ऑब्जेक्ट की स्थिति को साझा कर सकती हैं। प्रतिनिधि कार्य इतनी सफाई से साझा नहीं कर सकते, या यहां तक ​​कि राज्य को भी शामिल नहीं कर सकते।

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


1

"ईवेंटिंग" डिज़ाइन पैटर्न (जिसे ऑब्जर्वर पैटर्न के रूप में जाना जाता है) आपको प्रतिनिधि को एक ही हस्ताक्षर के कई तरीकों को संलग्न करने की अनुमति देता है। आप वास्तव में एक इंटरफेस के साथ ऐसा नहीं कर सकते।

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


1

सबसे बड़ी स्पष्टीकरण मैं प्रस्तुत कर सकता हूं:

  1. एक प्रतिनिधि एक फ़ंक्शन हस्ताक्षर को परिभाषित करता है - जो एक मेल खाने वाले फ़ंक्शन को स्वीकार करेगा और वह क्या लौटाएगा।
  2. एक इंटरफ़ेस फ़ंक्शंस, इवेंट्स, प्रॉपर्टीज़ और फ़ील्ड्स के पूरे सेट से संबंधित है।

इसलिए:

  1. जब आप एक ही हस्ताक्षर के साथ कुछ कार्यों को सामान्य बनाना चाहते हैं - एक प्रतिनिधि का उपयोग करें।
  2. जब आप किसी वर्ग के कुछ व्यवहार या गुणवत्ता को सामान्य बनाना चाहते हैं - एक इंटरफ़ेस का उपयोग करें।

1

घटनाओं पर आपका प्रश्न पहले से ही कवर किया गया है। और यह भी सच है, कि एक इंटरफ़ेस कई तरीकों को परिभाषित कर सकता है (लेकिन वास्तव में ज़रूरत नहीं है), जबकि एक फ़ंक्शन प्रकार केवल कभी-कभी एक व्यक्तिगत फ़ंक्शन पर अड़चन डालता है।

हालांकि वास्तविक अंतर यह है:

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

ज़रूर, लेकिन इसका क्या मतलब है?

आइए इस उदाहरण को लेते हैं (कोड हैक्स में है क्योंकि मेरा C # बहुत अच्छा नहीं है):

class Collection<T> {
    /* a lot of code we don't care about now */
    public function filter(predicate:T->Bool):Collection<T> { 
         //build a new collection with all elements e, such that predicate(e) == true
    }
    public function remove(e:T):Bool {
         //removes an element from this collection, if contained and returns true, false otherwise
    }
}

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

उदाहरण बहुत निर्मित है। लेकिन मुख्य समस्या यह है, कि c.filterकुछ फ़ंक्शन मान के साथ कॉल कोड तर्क के रूप में यह जानने का कोई तरीका नहीं है कि क्या तर्क उपयुक्त है (यानी अंतत: अपरिवर्तनीय का संरक्षण करेगा)। फ़ंक्शन मान बनाने वाले कोड को पता हो सकता है या नहीं, कि यह एक विधेय के रूप में व्याख्या किया जाएगा।

चलिए अब चीजों को बदलते हैं:

interface Predicate<T> {
    function test(value:T):Bool;
}
class Collection<T> {
    /* a lot of code we don't care about now */
    public function filter(predicate:Predicate<T>):Collection<T> { 
         //build a new collection with all elements e, such that predicate.test(e) == true
    }
    public function remove(e:T):Bool {
         //removes an element from this collection, if contained and returns true, false otherwise
    }
}

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

इसलिए कुछ शब्दों में उपरोक्त बातों को फिर से बताने के लिए:

  • इंटरफेस का मतलब नाममात्र टाइपिंग और इस तरह स्पष्ट संबंधों का उपयोग करना है
  • प्रतिनिधियों का अर्थ संरचनात्मक टाइपिंग और इस तरह निहित संबंधों का उपयोग करना है

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


1
यह ध्यान देने योग्य है कि "स्पष्ट रूप से इंटरफ़ेस को लागू करता है" ("नाममात्र उपप्रकार" के तहत) इंटरफ़ेस सदस्यों के स्पष्ट या अंतर्निहित कार्यान्वयन के समान नहीं है। सी # में, कक्षाओं को हमेशा स्पष्ट रूप से उन इंटरफेस को घोषित करना चाहिए जो वे लागू करते हैं, जैसा कि आप कहते हैं। लेकिन इंटरफ़ेस सदस्यों को स्पष्ट रूप से (जैसे, int IList.Count { get { ... } }) या अंतर्निहित रूप से ( public int Count { get { ... } }) लागू किया जा सकता है । यह चर्चा इस चर्चा के लिए प्रासंगिक नहीं है, लेकिन यह भ्रामक पाठकों से बचने के लिए उल्लेख के योग्य है।
फोग

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

1
  1. एक ईवेंटिंग डिज़ाइन पैटर्न से तात्पर्य एक संरचना से है जिसमें एक प्रकाशन और सदस्यता तंत्र शामिल है। घटनाओं को एक स्रोत द्वारा प्रकाशित किया जाता है, प्रत्येक ग्राहक को यह प्रकाशित आइटम की खुद की प्रति मिलती है और यह स्वयं के कार्यों के लिए जिम्मेदार है। यह एक शिथिल युग्मित तंत्र है क्योंकि प्रकाशक को यह जानने की भी जरूरत नहीं है कि ग्राहक हैं। अकेले ग्राहक होने अनिवार्य नहीं है (कोई भी हो सकता है)
  2. यदि एक प्रतिनिधि एक इंटरफेस की तुलना में उपयोग किया जाता है तो रचना "आसान" है। इंटरफेस एक वर्ग उदाहरण को "हैंडलर" ऑब्जेक्ट के रूप में परिभाषित करते हैं जहां एक रचना का निर्माण उदाहरण "हैंडलर है" के रूप में होता है इसलिए एक प्रतिनिधि का उपयोग करके इंस्टेंस की उपस्थिति कम प्रतिबंधित होती है और अधिक लचीली हो जाती है।
  3. नीचे टिप्पणी से मैंने पाया है कि मुझे अपनी पोस्ट में सुधार करने की आवश्यकता है, इसे संदर्भ के लिए देखें: http://bytes.com/topic/c-sharp/answers/252309-interface-vs-delegate

1
3. प्रतिनिधियों को अधिक कास्टिंग की आवश्यकता क्यों होगी, और एक लाभ के लिए सख्त युग्मन क्यों है? 4. आपको प्रतिनिधियों का उपयोग करने के लिए घटनाओं का उपयोग करने की आवश्यकता नहीं है, और प्रतिनिधियों के साथ यह एक विधि धारण करने जैसा है - आप जानते हैं कि यह वापसी प्रकार है और यह तर्क के प्रकार हैं। इसके अलावा, एक इंटरफ़ेस में घोषित विधि एक प्रतिनिधि से जुड़ी विधि की तुलना में त्रुटियों को संभालना आसान क्यों करेगी?
यम मार्कोविच

एक प्रतिनिधि के सिर्फ कल्पना के मामले में आप सही हैं। हालाँकि, घटना से निपटने के मामले में (कम से कम मेरा संदर्भ इससे संबंधित है) कि आपको कुछ प्रकार के ईवेंट के लिए विरासत की आवश्यकता होगी। कस्टम प्रकारों के लिए कंटेनर के रूप में कार्य करने के लिए, ताकि बदले में आप हमेशा एक मूल्य के लिए सुरक्षित रूप से परीक्षण करने में सक्षम हों। मूल्यों को संभालने से पहले अपने प्रकार को खोलना है।
कार्लो कुइप

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

1
सबसे पहले हम प्रतिनिधियों बनाम इंटरफेस के बारे में बात कर रहे थे, घटनाओं बनाम इंटरफेस के बारे में नहीं। दूसरे, एक EventHandler प्रतिनिधि के आधार पर एक इवेंट बेस बनाना केवल एक कन्वेंशन है, और अनिवार्य नहीं है। लेकिन फिर, यहाँ की घटनाओं के बारे में बात करने से इस तरह की बात याद आती है। आपकी दूसरी टिप्पणी के अनुसार - अपवादों की C # में जाँच नहीं की गई है। आप जावा के साथ भ्रमित हो रहे हैं (जो संयोगवश प्रतिनिधियों के पास भी नहीं है)।
यम मारकोविच

महत्वपूर्ण अंतर बताते हुए इस विषय पर एक सूत्र मिला: bytes.com/topic/c-sharp/answers/252309-interface-vs-delegate
कार्लो
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.