डबल डिस्पैच: Difference between revisions

From Vigyanwiki
No edit summary
No edit summary
Line 133: Line 133:
theAsteroidReference.CollideWith(theApolloSpacecraft);
theAsteroidReference.CollideWith(theApolloSpacecraft);
</syntaxhighlight>
</syntaxhighlight>
प्रिंट <code>ExplodingAsteroid hit a SpaceShip</code> और <code>ExplodingAsteroid hit an ApolloSpacecraft</code>, फिर से जैसी कि उम्मीद थी। चूँकि, निम्नलिखित कोड इच्छानुसार काम नहीं करता है:
प्रिंट <code>ExplodingAsteroid hit a SpaceShip</code> और <code>ExplodingAsteroid hit an ApolloSpacecraft</code>, फिर से जैसी कि आशा थी। चूँकि, निम्नलिखित कोड इच्छानुसार काम नहीं करता है:
<syntaxhighlight lang="cpp">
<syntaxhighlight lang="cpp">
SpaceShip& theSpaceShipReference = theApolloSpacecraft;
SpaceShip& theSpaceShipReference = theApolloSpacecraft;
Line 507: Line 507:
डबल-डिस्पैच के संबंध में, एफिल डिजाइनर और प्रोग्रामर को क्लास रूटीन को एजेंट बनाकर और फिर डायरेक्ट ऑब्जेक्ट फीचर कॉल करने के अतिरिक्त उन एजेंटों को पास करके उनकी कक्षाओं से क्लास रूटीन को अलग करके प्रत्यक्ष ऑब्जेक्ट-टू-ऑब्जेक्ट ज्ञान के स्तर को हटाने की अनुमति देता है। एजेंटों के पास विशिष्ट हस्ताक्षर और संभावित परिणाम (प्रश्नों के स्थितियों में) भी होते हैं, जिससे वे विशिष्ट वस्तु विवरण छोड़े बिना आदर्श स्थैतिक प्रकार की जांच करने वाले वाहन बन जाते हैं। एजेंट पूरी तरह से बहुरूपी होते हैं, इसलिए परिणामी कोड में केवल अपना स्थानीय कार्य पूरा करने के लिए आवश्यक विशिष्ट ज्ञान होता है। अन्यथा, कई सह-संस्करण वस्तुओं के आसपास विशिष्ट आंतरिक वर्ग सुविधा ज्ञान फैलने से कोई रखरखाव बोझ नहीं बढ़ता है। एजेंटों का उपयोग और यांत्रिकी यह सुनिश्चित करती है।
डबल-डिस्पैच के संबंध में, एफिल डिजाइनर और प्रोग्रामर को क्लास रूटीन को एजेंट बनाकर और फिर डायरेक्ट ऑब्जेक्ट फीचर कॉल करने के अतिरिक्त उन एजेंटों को पास करके उनकी कक्षाओं से क्लास रूटीन को अलग करके प्रत्यक्ष ऑब्जेक्ट-टू-ऑब्जेक्ट ज्ञान के स्तर को हटाने की अनुमति देता है। एजेंटों के पास विशिष्ट हस्ताक्षर और संभावित परिणाम (प्रश्नों के स्थितियों में) भी होते हैं, जिससे वे विशिष्ट वस्तु विवरण छोड़े बिना आदर्श स्थैतिक प्रकार की जांच करने वाले वाहन बन जाते हैं। एजेंट पूरी तरह से बहुरूपी होते हैं, इसलिए परिणामी कोड में केवल अपना स्थानीय कार्य पूरा करने के लिए आवश्यक विशिष्ट ज्ञान होता है। अन्यथा, कई सह-संस्करण वस्तुओं के आसपास विशिष्ट आंतरिक वर्ग सुविधा ज्ञान फैलने से कोई रखरखाव बोझ नहीं बढ़ता है। एजेंटों का उपयोग और यांत्रिकी यह सुनिश्चित करती है।


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


==यह भी देखें==
==यह भी देखें==

Revision as of 00:23, 5 August 2023

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

डैन इंगल्स ने सबसे पहले स्मॉलटॉक में डबल डिस्पैचिंग का उपयोग करने का विधि बताया, इसे एकाधिक बहुरूपता कहा गया।[1]


अवलोकन

सामान्य समस्या यह है कि किसी संदेश को न केवल प्राप्तकर्ता पर बल्कि तर्कों पर भी निर्भर करते हुए विभिन्न विधियों से कैसे भेजा जाए।

उस अंत तक, सीएलओएस जैसी प्रणालियाँ एकाधिक प्रेषण प्रयुक्त करती हैं। डबल डिस्पैच एक और समाधान है जो धीरे-धीरे उन प्रणालियों पर बहुरूपता को कम करता है जो मल्टीपल डिस्पैच का समर्थन नहीं करते हैं।

स्थितियों का प्रयोग करें

डबल डिस्पैच उन स्थितियों में उपयोगी है जहां गणना का विकल्प उसके तर्कों के रनटाइम प्रकारों पर निर्भर करता है। उदाहरण के लिए प्रोग्रामर निम्नलिखित स्थितियों में डबल डिस्पैच का उपयोग कर सकता है:

  • वस्तुओं के मिश्रित सेट को क्रमबद्ध करना: एल्गोरिदम के लिए आवश्यक है कि वस्तुओं की सूची को कुछ विहित क्रम में क्रमबद्ध किया जाए। यह निर्णय लेने के लिए कि क्या तत्व दूसरे तत्व से पहले आता है, दोनों प्रकार के ज्ञान और संभवतः क्षेत्रों के कुछ सबसेट की आवश्यकता होती है।
  • अनुकूली टकराव एल्गोरिदम के लिए सामान्यतः आवश्यकता होती है कि विभिन्न वस्तुओं के बीच टकराव को अलग-अलग विधियों से नियंत्रित किया जाए। विशिष्ट उदाहरण खेल के स्थिति में है जहां अंतरिक्ष यान और क्षुद्रग्रह के बीच टकराव की गणना अंतरिक्ष यान और अंतरिक्ष स्टेशन के बीच टकराव से अलग विधियों से की जाती है।[2]
  • पेंटिंग एल्गोरिदम जिन्हें ओवरलैपिंग स्प्राइट (कंप्यूटर ग्राफिक्स) के प्रतिच्छेदन बिंदुओं को अलग विधियों से प्रस्तुत करने की आवश्यकता होती है।
  • कार्मिक प्रबंधन प्रणालियाँ विभिन्न कर्मियों को विभिन्न प्रकार की नौकरियाँ भेज सकती हैं। A schedule एल्गोरिदम जो व्यक्ति को अकाउंटेंट के रूप में टाइप की गई वस्तु और इंजीनियरिंग के रूप में टाइप की गई नौकरी की वस्तु देता है, उस नौकरी के लिए उस व्यक्ति की शेड्यूलिंग को अस्वीकार कर देता है।
  • इवेंट हैंडलिंग प्रणाली जो सही इवेंट हैंडलिंग रूटीन को कॉल करने के लिए इवेंट प्रकार और रिसेप्टर ऑब्जेक्ट के प्रकार दोनों का उपयोग करते हैं।
  • ताला और चाबी प्रणालियाँ जहाँ कई प्रकार के ताले और कई प्रकार की चाबियाँ होती हैं और प्रत्येक प्रकार की चाबी कई प्रकार के ताले खोलती है। आपको न केवल इसमें सम्मिलित वस्तुओं के प्रकारों को जानने की आवश्यकता है, बल्कि किसी विशेष कुंजी के बारे में जानकारी का सबसेट जो यह देखने के लिए प्रासंगिक है कि क्या कोई विशेष कुंजी किसी विशेष लॉक को खोलती है, विभिन्न लॉक प्रकारों के बीच भिन्न होती है।

सामान्य मुहावरा

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

रूबी में उदाहरण

सामान्य उपयोग की स्थितियां डिस्प्ले पोर्ट पर ऑब्जेक्ट प्रदर्शित करना है जो स्क्रीन या प्रिंटर हो सकता है, या पूरी तरह से कुछ और जो अभी तक उपस्थित नहीं है। यह उन विभिन्न मीडिया से निपटने का अनुभवहीन कार्यान्वयन है।

class Rectangle
  def display_on(port)
    # selects the right code based on the object class
    case port
      when DisplayPort
        # code for displaying on DisplayPort
      when PrinterPort
        # code for displaying on PrinterPort
      when RemotePort
        # code for displaying on RemotePort
    end
  end
end

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

एक अधिक स्वच्छ और अधिक रख-रखाव योग्य समाधान यह है कि इस बार माध्यम पर वस्तु को प्रदर्शित करने के लिए सही विधि का चयन करने के लिए दूसरा प्रेषण किया जाए:

class Rectangle
  def display_on(port)
    # second dispatch
    port.display_rectangle(self)
  end
end

class Oval
  def display_on(port)
    # second dispatch
    port.display_oval(self)
  end
end

class DisplayPort
  def display_rectangle(object)
    # code for displaying a rectangle on a DisplayPort
  end
  def display_oval(object)
    # code for displaying an oval on a DisplayPort
  end
  # ...
end

class PrinterPort
  def display_rectangle(object)
    # code for displaying a rectangle on a PrinterPort
  end
  def display_oval(object)
    # code for displaying an oval on a PrinterPort
  end
  # ...
end


C++ में दोहरा प्रेषण

पहली दृष्टी में, दोहरा प्रेषण फलन ओवरलोडिंग का स्वाभाविक परिणाम प्रतीत होता है। फलन ओवरलोडिंग कॉल किए गए फलन को तर्क के प्रकार पर निर्भर करने की अनुमति देता है। चूँकि, फलन ओवरलोडिंग, नाम मंगलिंग का उपयोग करके संकलन समय पर किया जाता है, जहां फलन का आंतरिक नाम तर्क के प्रकार को एन्कोड करता है। उदाहरण के लिए फलन foo(int) आंतरिक रूप से बुलाया जा सकता है__foo_i और फलन foo(double) बुलाया जा सकता है__foo_d. इस प्रकार, कोई नाम टकराव नहीं है, और कोई वर्चुअल टेबल लुकअप नहीं है। इसके विपरीत, डायनेमिक डिस्पैच कॉलिंग ऑब्जेक्ट के प्रकार पर आधारित होता है, जिसका अर्थ है कि यह फलन ओवरलोडिंग के अतिरिक्त आभासी कार्य (ओवरराइडिंग) का उपयोग करता है, और इसके परिणामस्वरूप व्यवहार्य लुकअप होता है। किसी गेम में टकराव के C++ में लिखे गए निम्नलिखित उदाहरण पर विचार करें:

class SpaceShip {};
class ApolloSpacecraft : public SpaceShip {};

class Asteroid {
public:
  virtual void CollideWith(SpaceShip&) {
    std::cout << "Asteroid hit a SpaceShip\n";
  }
  virtual void CollideWith(ApolloSpacecraft&) {
    std::cout << "Asteroid hit an ApolloSpacecraft\n";
  }
};

class ExplodingAsteroid : public Asteroid {
public:
  void CollideWith(SpaceShip&) override {
    std::cout << "ExplodingAsteroid hit a SpaceShip\n";
  }
  void CollideWith(ApolloSpacecraft&) override {
    std::cout << "ExplodingAsteroid hit an ApolloSpacecraft\n";
  }
};

यदि आपके पास है:

Asteroid theAsteroid;
SpaceShip theSpaceShip;
ApolloSpacecraft theApolloSpacecraft;

फिर, फलन ओवरलोडिंग के कारण,

theAsteroid.CollideWith(theSpaceShip); 
theAsteroid.CollideWith(theApolloSpacecraft);

क्रमशः प्रिंट करेगा, Asteroid hit a SpaceShip और Asteroid hit an ApolloSpacecraft, बिना किसी गतिशील प्रेषण का उपयोग किए। आगे:

ExplodingAsteroid theExplodingAsteroid;
theExplodingAsteroid.CollideWith(theSpaceShip); 
theExplodingAsteroid.CollideWith(theApolloSpacecraft);

छापूंगा ExplodingAsteroid hit a SpaceShip और ExplodingAsteroid hit an ApolloSpacecraft क्रमशः, फिर से गतिशील प्रेषण के बिना।

एक के संदर्भ में Asteroid, गतिशील प्रेषण का उपयोग किया जाता है, और यह कोड:

Asteroid& theAsteroidReference = theExplodingAsteroid;
theAsteroidReference.CollideWith(theSpaceShip); 
theAsteroidReference.CollideWith(theApolloSpacecraft);

प्रिंट ExplodingAsteroid hit a SpaceShip और ExplodingAsteroid hit an ApolloSpacecraft, फिर से जैसी कि आशा थी। चूँकि, निम्नलिखित कोड इच्छानुसार काम नहीं करता है:

SpaceShip& theSpaceShipReference = theApolloSpacecraft;
theAsteroid.CollideWith(theSpaceShipReference);
theAsteroidReference.CollideWith(theSpaceShipReference);

वांछित व्यवहार इन कॉलों को उस फलन से बांधना है जो लेता है theApolloSpacecraft इसके तर्क के रूप में, क्योंकि यह चर का तात्कालिक प्रकार है, जिसका अर्थ है कि अपेक्षित आउटपुट होगा Asteroid hit an ApolloSpacecraft और ExplodingAsteroid hit an ApolloSpacecraft. चूँकि, आउटपुट वास्तव में है Asteroid hit a SpaceShip और ExplodingAsteroid hit a SpaceShip. समस्या यह है कि, जबकि वर्चुअल फलन C++ में गतिशील रूप से भेजे जाते हैं, फलन ओवरलोडिंग स्थिर रूप से की जाती है।

ऊपर वर्णित समस्या को दोहरे प्रेषण का अनुकरण करके हल किया जा सकता है, उदाहरण के लिए आगंतुक पैटर्न का उपयोग करके। मान लीजिए कि उपस्थिता कोड को बढ़ाया गया है जिससे दोनों SpaceShip और ApolloSpacecraft फलन दिया गया है

virtual void CollideWith(Asteroid& inAsteroid) {
  inAsteroid.CollideWith(*this);
}

फिर, जबकि पिछला उदाहरण अभी भी सही ढंग से काम नहीं करता है, कॉल को फिर से फ्रेम करना ताकि अंतरिक्ष यान एजेंट हो, हमें वांछित व्यवहार देता है:

SpaceShip& theSpaceShipReference = theApolloSpacecraft;
Asteroid& theAsteroidReference = theExplodingAsteroid;
theSpaceShipReference.CollideWith(theAsteroid);
theSpaceShipReference.CollideWith(theAsteroidReference);

यह प्रिंट कर लेता है Asteroid hit an ApolloSpacecraft और ExplodingAsteroid hit an ApolloSpacecraft, आशा के अनुसार कुंजी वह है theSpaceShipReference.CollideWith(theAsteroidReference); रन टाइम पर निम्न कार्य करता है:

  1. theSpaceShipReference संदर्भ है, इसलिए C++ vtable में सही विधि ढूंढता है। इस स्थितियों में, यह कॉल करेगा ApolloSpacecraft::CollideWith(Asteroid&).
  2. अंदर ApolloSpacecraft::CollideWith(Asteroid&), inAsteroid संदर्भ है, इसलिए inAsteroid.CollideWith(*this) इसके परिणामस्वरूप एक और वेटेबल लुकअप होगा। इस स्थितियों में, inAsteroid एक का संदर्भ है ExplodingAsteroid इसलिए ExplodingAsteroid::CollideWith(ApolloSpacecraft&) बुलाया जाएगा।

C# में डबल प्रेषण

C शार्प (प्रोग्रामिंग भाषा) C# में, किसी तर्क को स्वीकार करते हुए इंस्टेंस विधि को कॉल करते समय, विज़िटर पैटर्न को नियोजित किए बिना एकाधिक प्रेषण प्राप्त किया जा सकता है। यह तर्क को गतिशील बनाते हुए पारंपरिक बहुरूपता का उपयोग करके किया जाता है।[3] रन-टाइम बाइंडर रन-टाइम पर उचित विधि ओवरलोड का चयन करेगा। यह निर्णय ऑब्जेक्ट इंस्टेंस के रन-टाइम प्रकार (बहुरूपता) के साथ-साथ तर्क के रन-टाइम प्रकार को भी ध्यान में रखेगा।

एफिल में दोहरा प्रेषण

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

आकृति के विभिन्न रूपों और सतह को चित्रित करने वाले समस्या डोमेन पर विचार करें, जिस पर आकृति बनाई जाए। SHAPE और SURFACE दोनों अपने आप में `ड्रा' नामक फलन के बारे में जानते हैं, लेकिन दूसरे में नहीं हम चाहते हैं कि दो प्रकार की वस्तुएं विज़िटर पैटर्न का उपयोग करके डबल-डिस्पैच में एक-दूसरे के साथ सह-भिन्न रूप से वार्तालाप करें।

चुनौती बहुरूपी सतह को अपने ऊपर बहुरूपी आकार बनाने के लिए प्राप्त करना है।

आउटपुट

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

draw a red POLYGON on ETCHASKETCH
draw a red POLYGON on GRAFFITI_WALL
draw a grey RECTANGLE on ETCHASKETCH
draw a grey RECTANGLE on GRAFFITI_WALL
draw a green QUADRILATERAL on ETCHASKETCH
draw a green QUADRILATERAL on GRAFFITI_WALL
draw a blue PARALLELOGRAM on ETCHASKETCH
draw a blue PARALLELOGRAM on GRAFFITI_WALL
draw a yellow POLYGON on ETCHASKETCH
draw a yellow POLYGON on GRAFFITI_WALL
draw a purple RECTANGLE on ETCHASKETCH
draw a purple RECTANGLE on GRAFFITI_WALL


सेटअप

SHAPE या SURFACE को देखने से पहले, हमें अपने डबल-डिस्पैच के उच्च स्तरीय डिकौपल्ड उपयोग की जांच करने की आवश्यकता है।

विज़िटर पैटर्न

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

नीचे दिए गए हमारे उदाहरण में, हम बहुरूपी SHAPE वस्तुओं की सूची बनाते हैं, उनमें से प्रत्येक को बहुरूपी सतह के साथ देखते हैं, और सतह पर आकृति बनाने के लिए कहते हैं।

make
			-- Print shapes on surfaces.
		local
			l_shapes: ARRAYED_LIST [SHAPE]
			l_surfaces: ARRAYED_LIST [SURFACE]
		do
			create l_shapes.make (6)
			l_shapes.extend (create {POLYGON}.make_with_color ("red"))
			l_shapes.extend (create {RECTANGLE}.make_with_color ("grey"))
			l_shapes.extend (create {QUADRILATERAL}.make_with_color ("green"))
			l_shapes.extend (create {PARALLELOGRAM}.make_with_color ("blue"))
			l_shapes.extend (create {POLYGON}.make_with_color ("yellow"))
			l_shapes.extend (create {RECTANGLE}.make_with_color ("purple"))

			create l_surfaces.make (2)
			l_surfaces.extend (create {ETCHASKETCH}.make)
			l_surfaces.extend (create {GRAFFITI_WALL}.make)

			across l_shapes as ic_shapes loop
				across l_surfaces as ic_surfaces loop
					ic_surfaces.item.drawing_agent (ic_shapes.item.drawing_data_agent)
				end
			end
		end

हम SHAPE और SURFACE वस्तुओं का संग्रह बनाकर प्रारंभ करते हैं। फिर हम सूचियों (SHAPE) में से एक पर पुनरावृत्ति करते हैं, जिससे दूसरे (SURFACE) के तत्वों को बारी-बारी से उनमें से प्रत्येक पर जाने की अनुमति मिलती है। उपरोक्त उदाहरण कोड में, SURFACE ऑब्जेक्ट SHAPE ऑब्जेक्ट पर जा रहे हैं।

कोड {SURFACE} पर बहुरूपी कॉल करता है। 'ड्राइंग_एजेंट' के माध्यम से अप्रत्यक्ष रूप से ड्रा करें, जो डबल-डिस्पैच पैटर्न की पहली कॉल (डिस्पैच) है। यह अप्रत्यक्ष और बहुरूपी एजेंट (`ड्राइंग_डेटा_एजेंट') पास करता है, जिससे हमारे विज़िटर कोड को केवल दो चीजों के बारे में जानने की अनुमति मिलती है:

  • सतह का ड्राइंग एजेंट क्या है (उदाहरण के लिए लाइन #21 पर al_surface.drawing_agent)?
  • आकृति का ड्राइंग डेटा एजेंट क्या है (उदाहरण के लिए लाइन #21 पर al_shape.drawing_data_agent)?

चूँकि SURFACE और SHAPE दोनों ही अपने स्वयं के एजेंटों को परिभाषित करते हैं, इसलिए हमारे विज़िटर कोड को यह जानने से मुक्ति मिल जाती है कि बहुरूपी या अन्यथा, करने के लिए उपयुक्त कॉल क्या है। इनडायरेक्शन और डिकॉउलिंग का यह स्तर C, C++ और जावा जैसी अन्य सामान्य भाषाओं में किसी प्रकार के प्रतिबिंब (कंप्यूटर विज्ञान) या हस्ताक्षर मिलान के साथ फीचर ओवरलोडिंग को छोड़कर सरलता से प्राप्त नहीं किया जा सकता है।

सतह

बहुरूपी कॉल के अन्दर {SURFACE}.draw एजेंट के लिए कॉल है, जो डबल-डिस्पैच पैटर्न में दूसरी बहुरूपी कॉल या प्रेषण बन जाता है।

	deferred class
		SURFACE
	
	feature {NONE} -- Initialization
	
		make
				-- Initialize Current.
			do
				drawing_agent := agent draw
			end
	
	feature -- Access

		drawing_agent: PROCEDURE [ANY, TUPLE [STRING, STRING]]
				-- Drawing agent of Current.
	
	feature {NONE} -- Implementation
	
		draw (a_data_agent: FUNCTION [ANY, TUPLE, TUPLE [name, color: STRING]])
				-- Draw `a_shape' on Current.
			local
				l_result: TUPLE [name, color: STRING]
			do
				l_result := a_data_agent (Void)
				print ("draw a " + l_result.color + " " + l_result.name + " on " + type + "%N")
			end
	
		type: STRING
				-- Type name of Current.
			deferred end
	
	end

पंक्ति #19 में एजेंट तर्क और पंक्ति #24 में कॉल दोनों बहुरूपी और वियुग्मित हैं। एजेंट को अलग कर दिया गया है क्योंकि {SURFACE}.draw फीचर को पता नहीं है कि `a_data_agent' किस वर्ग पर आधारित है। यह बताने का कोई विधि नहीं है कि ऑपरेशन एजेंट किस वर्ग से लिया गया था, इसलिए इसे SHAPE या उसके वंशजों में से किसी एक से आना आवश्यक नहीं है। यह अन्य भाषाओं की एकल विरासत, गतिशील और बहुरूपी बंधन पर एफिल एजेंटों का विशिष्ट लाभ है।

रन-टाइम पर एजेंट गतिशील रूप से बहुरूपी होता है क्योंकि ऑब्जेक्ट को उसी क्षण बनाया जाता है, जब इसकी आवश्यकता होती है, गतिशील रूप से, जहां उस समय ऑब्जेक्टिफाइड रूटीन का संस्करण निर्धारित किया जाता है। एकमात्र दृढ़ता से बंधा हुआ ज्ञान एजेंट हस्ताक्षर के परिणाम प्रकार का है - अर्थात - दो तत्वों के साथ TUPLE नाम दिया गया है। चूँकि, यह विशिष्ट आवश्यकता संलग्न सुविधा की मांग पर आधारित है (उदाहरण के लिए पंक्ति #25, SURFACE की 'ड्रा' सुविधा को पूरा करने के लिए TUPLE के नामित तत्वों का उपयोग करती है), जो आवश्यक है और इसे टाला नहीं गया है (और शायद नहीं भी किया जा सकता है)।

अंत में, ध्यान दें कि किसी भी ग्राहक को केवल 'ड्राइंग_एजेंट' सुविधा कैसे निर्यात की जाती है! इसका अर्थ यह है कि विज़िटर पैटर्न कोड (जो इस वर्ग का एकमात्र ग्राहक है) को अपना काम पूरा करने के लिए केवल एजेंट के बारे में जानना होगा (उदाहरण के लिए विज़िट की गई वस्तुओं पर प्रयुक्त सुविधा के रूप में एजेंट का उपयोग करना)।

आकार

SHAPE वर्ग के पास जो कुछ खींचा जाता है उसका आधार होता है (उदाहरण के लिए डेटा खींचना), शायद किसी सतह पर, लेकिन ऐसा होना आवश्यक नहीं है। फिर से, एजेंट SHAPE के साथ सह-संस्करण संबंध को यथासंभव विघटित करने के लिए आवश्यक अप्रत्यक्ष और वर्ग अज्ञेयवादी प्रदान करते हैं।

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

	deferred class
		SHAPE
	
	feature {NONE} -- Initialization
	
		make_with_color (a_color: like color)
				-- Make with `a_color' as `color'.
			do
				color := a_color
				drawing_data_agent := agent drawing_data
			ensure
				color_set: color.same_string (a_color)
			end

	feature -- Access
	
		drawing_data_agent: FUNCTION [ANY, TUPLE, like drawing_data]
				-- Data agent for drawing.
	
	feature {NONE} -- Implementation
	
		drawing_data: TUPLE [name: like name; color: like color]
				-- Data needed for drawing of Current.
			do
				Result := [name, color]
			end
	
		name: STRING
				-- Object name of Current.
			deferred end
	
		color: STRING
				-- Color of Current.

	end


क्लासिक अंतरिक्ष यान उदाहरण

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

नीचे दिए गए उदाहरण में, हमारे यूएसएस एंटरप्राइज और यूएसएस एक्सेलसियर का आउटपुट भ्रमण होगा:

Starship Enterprise changes position from A-001 to A-002.
Starship Enterprise takes evasive action, avoiding Asteroid `Rogue 1'!
Starship Enterprise changes position from A-002 to A-003.
Starship Enterprise takes evasive action, avoiding Asteroid `Rogue 2'!
Starship Enterprise beams a science team to Starship Excelsior as they pass!
Starship Enterprise changes position from A-003 to A-004.
Starship Excelsior changes position from A-003 to A-005.
Starship Enterprise takes evasive action, avoiding Asteroid `Rogue 3'!
Starship Excelsior is near Space Station Deep Space 9 and is dockable.
Starship Enterprise changes position from A-004 to A-005.
Starship Enterprise beams a science team to Starship Excelsior as they pass!
Starship Enterprise is near Space Station Deep Space 9 and is dockable.


विजिटर

क्लासिक स्पेसशिप उदाहरण के लिए विज़िटर के पास डबल-डिस्पैच तंत्र भी है।

make
		-- Allow SPACESHIP objects to visit and move about in a universe.
	local
		l_universe: ARRAYED_LIST [SPACE_OBJECT]
		l_enterprise,
		l_excelsior: SPACESHIP
	do
		create l_enterprise.make_with_name ("Enterprise", "A-001")
		create l_excelsior.make_with_name ("Excelsior", "A-003")
		create l_universe.make (0)
		l_universe.force (l_enterprise)
		l_universe.force (create {ASTEROID}.make_with_name ("Rogue 1", "A-002"))
		l_universe.force (create {ASTEROID}.make_with_name ("Rogue 2", "A-003"))
		l_universe.force (l_excelsior)
		l_universe.force (create {ASTEROID}.make_with_name ("Rogue 3", "A-004"))
		l_universe.force (create {SPACESTATION}.make_with_name ("Deep Space 9", "A-005"))
		visit (l_enterprise, l_universe)
		l_enterprise.set_position ("A-002")
		visit (l_enterprise, l_universe)
		l_enterprise.set_position ("A-003")
		visit (l_enterprise, l_universe)
		l_enterprise.set_position ("A-004")
		l_excelsior.set_position ("A-005")
		visit (l_enterprise, l_universe)
		visit (l_excelsior, l_universe)
		l_enterprise.set_position ("A-005")
		visit (l_enterprise, l_universe)
	end
feature {NONE} -- Implementation
visit (a_object: SPACE_OBJECT; a_universe: ARRAYED_LIST [SPACE_OBJECT])
		-- `a_object' visits `a_universe'.
	do
		across a_universe as ic_universe loop
			check attached {SPACE_OBJECT} ic_universe.item as al_universe_object then
				a_object.encounter_agent.call ([al_universe_object.sensor_data_agent])
			end
		end
	end

डबल-डिस्पैच को पंक्ति #35 में देखा जा सकता है, जहां दो अप्रत्यक्ष एजेंट एक-दूसरे के साथ पूर्ण बहुरूपी संगीत फलन में काम करते हुए दो सह-संस्करण कॉल प्रदान करने के लिए मिलकर काम कर रहे हैं। `विज़िट' फ़ीचर के `a_ऑब्जेक्ट' में `एनकाउंटर_एजेंट' होता है जिसे 'अल_यूनिवर्स_ऑब्जेक्ट' से आने वाले `सेंसर_डेटा_एजेंट' के सेंसर डेटा के साथ बुलाया जाता है।

इस विशेष उदाहरण का अन्य दिलचस्प हिस्सा SPACE_OBJECT वर्ग और इसकी 'मुठभेड़' सुविधा है:

विजिटर गतिविधि

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

अन्य सभी डेटा को {NONE} में निर्यात किया जाता है। यह प्राइवेट के C, C++ और Java स्कोप के समान है। गैर-निर्यातित सुविधाओं के रूप में, डेटा और रूटीन का उपयोग प्रत्येक SPACE_OBJECT द्वारा केवल आंतरिक रूप से किया जाता है।

अंत में, ध्यान दें कि 'प्रिंट' के लिए एनकाउंटर कॉल में SPACE_OBJECT के संभावित वंशज वर्गों के बारे में विशिष्ट जानकारी सम्मिलित नहीं है! विरासत में इस स्तर पर पाई जाने वाली एकमात्र चीज़ सामान्य संबंधपरक पहलू हैं जो पूरी तरह से सामान्य SPACE_OBJECT की विशेषताओं और दिनचर्या से ज्ञात की जा सकने वाली बातों पर आधारित हैं। तथ्य यह है कि स्टार जहाजों, अंतरिक्ष स्टेशनों और क्षुद्रग्रहों के बारे में हम जो जानते हैं या कल्पना करते हैं, उसके आधार पर 'प्रिंट' का आउटपुट हमारे लिए मनुष्य के रूप में समझ में आता है, यह केवल तार्किक योजना या संयोग है। SPACE_OBJECT को उसके वंशजों के किसी विशिष्ट ज्ञान के साथ प्रोग्राम नहीं किया गया है।

deferred class
SPACE_OBJECT
feature {NONE} -- Initialization
make_with_name (a_name: like name; a_position: like position)
    -- Initialize Current with `a_name' and `a_position'.
  do
    name := a_name
    position := a_position
    sensor_data_agent := agent sensor_data
    encounter_agent := agent encounter
  ensure
    name_set: name.same_string (a_name)
    position_set: position.same_string (a_position)
  end
feature -- Access
encounter_agent: PROCEDURE [ANY, TUPLE]
    -- Agent for managing encounters with Current.
sensor_data_agent: FUNCTION [ANY, TUPLE, attached like sensor_data_anchor]
    -- Agent for returning sensor data of Current.
feature -- Settings
set_position (a_position: like position)
    -- Set `position' with `a_position'.
  do
    print (type + " " + name + " changes position from " + position + " to " + a_position + ".%N")
    position := a_position
  ensure
    position_set: position.same_string (a_position)
  end
feature {NONE} -- Implementation
encounter (a_sensor_agent: FUNCTION [ANY, TUPLE, attached like sensor_data_anchor])
    -- Detect and report on collision status of Current with `a_radar_agent'.
  do
    a_sensor_agent.call ([Void])
    check attached {like sensor_data_anchor} a_sensor_agent.last_result as al_sensor_data then
      if not name.same_string (al_sensor_data.name) then
        if (position.same_string (al_sensor_data.position)) then
          if ((al_sensor_data.is_dockable and is_dockable) and
              (is_manned and al_sensor_data.is_manned) and
              (is_manueverable and al_sensor_data.is_not_manueverable)) then
            print (type + " " + name + " is near " + al_sensor_data.type + " " +
                al_sensor_data.name + " and is dockable.%N")
          elseif ((is_dockable and al_sensor_data.is_dockable) and
                (is_manned and al_sensor_data.is_manned) and
                (is_manueverable and al_sensor_data.is_manueverable)) then
            print (type + " " + name + " beams a science team to " + al_sensor_data.type + " " +
                al_sensor_data.name + " as they pass!%N")
          elseif (is_manned and al_sensor_data.is_not_manned) then
            print (type + " " + name + " takes evasive action, avoiding " +
                al_sensor_data.type + " `" + al_sensor_data.name + "'!%N")
          end
        end
      end
    end
  end
name: STRING
    -- Name of Current.
type: STRING
    -- Type of Current.
  deferred
  end
position: STRING
    -- Position of Current.
is_dockable: BOOLEAN
    -- Is Current dockable with another manned object?
  deferred
  end
is_manned: BOOLEAN
    -- Is Current a manned object?
  deferred
  end
is_manueverable: BOOLEAN
    -- Is Current capable of being moved?
  deferred
  end
sensor_data: attached like sensor_data_anchor
    -- Sensor data of Current.
  do
      Result := [name, type, position, is_dockable, not is_dockable, is_manned, not is_manned, is_manueverable, not is_manueverable]
    end

  sensor_data_anchor: detachable TUPLE [name, type, position: STRING; is_dockable, is_not_dockable, is_manned, is_not_manned, is_manueverable, is_not_manueverable: BOOLEAN]
      -- Sensor data type anchor of Current.

end

SPACE_OBJECT के तीन वंशज वर्ग हैं:

SPACE_OBJECT
ASTEROID
SPACESHIP
SPACESTATION

हमारे उदाहरण में, क्षुद्रग्रह वर्ग का उपयोग 'दुष्ट' वस्तुओं के लिए किया जाता है, दो सितारा जहाजों के लिए स्पेसशिप और डीप स्पेस नाइन के लिए स्पेसस्टेशन का उपयोग किया जाता है। प्रत्येक वर्ग में, एकमात्र विशेषज्ञता 'प्रकार' सुविधा और वस्तु के कुछ गुणों की सेटिंग है। रचना क्रम में 'नाम' के साथ-साथ 'पद' भी दिया जाता है।

उदाहरण के लिए: नीचे स्पेसशिप उदाहरण है।

class
SPACESHIP
inherit
SPACE_OBJECT
create
make_with_name
feature {NONE} -- Implementation
type: STRING = "Starship"
  -- <Precursor>
is_dockable: BOOLEAN = True
  -- <Precursor>
is_manned: BOOLEAN = True
  -- <Precursor>
is_manueverable: BOOLEAN = True
  -- <Precursor>
end

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

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

एफिल उदाहरण निष्कर्ष

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

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

यह भी देखें

  • विज़िटर पैटर्न
  • एकाधिक प्रेषण
  • हैश फलन#ट्रिविअल हैश फलन
  • आभासी तालिका

संदर्भ

  1. A Simple Technique for Handling Multiple Polymorphism. In Proceedings of OOPSLA '86, Object–Oriented Programming Systems, Languages and Applications, pages 347–349, November 1986. Printed as SIGPLAN Notices, 21(11). ISBN 0-89791-204-7
  2. More Effective C++ by Scott Meyers(Addison-Wesley, 1996)
  3. "टाइप डायनामिक का उपयोग करना (सी# प्रोग्रामिंग गाइड)". Microsoft Developer Network. Microsoft. 30 Sep 2009. Retrieved 25 May 2016. ... Overload resolution occurs at run time instead of at compile time if one or more of the arguments in a method call have the type dynamic ...