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

From Vigyanwiki
No edit summary
 
(2 intermediate revisions by 2 users not shown)
Line 515: Line 515:
==संदर्भ==
==संदर्भ==
{{Reflist}}
{{Reflist}}
{{DEFAULTSORT:Double Dispatch}}[[Category: सॉफ़्टवेयर डिज़ाइन पैटर्न]] [[Category: विधि (कंप्यूटर प्रोग्रामिंग)]] [[Category: उदाहरण C++ कोड वाले लेख]]
{{DEFAULTSORT:Double Dispatch}}


 
[[Category:Created On 25/07/2023|Double Dispatch]]
 
[[Category:Machine Translated Page|Double Dispatch]]
[[Category: Machine Translated Page]]
[[Category:Pages with script errors|Double Dispatch]]
[[Category:Created On 25/07/2023]]
[[Category:Pages with syntax highlighting errors]]
[[Category:Templates Vigyan Ready|Double Dispatch]]
[[Category:उदाहरण C++ कोड वाले लेख|Double Dispatch]]
[[Category:विधि (कंप्यूटर प्रोग्रामिंग)|Double Dispatch]]
[[Category:सॉफ़्टवेयर डिज़ाइन पैटर्न|Double Dispatch]]

Latest revision as of 11:31, 11 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

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

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

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

एफिल में डबल डिस्पैच

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

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

आउटपुट

नीचे दिया गया आउटपुट उदाहरण दो 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} पर बहुरूपी कॉल करता है। Drawing_agent के माध्यम से अप्रत्यक्ष रूप से ड्रा करें, जो डबल-डिस्पैच पैटर्न की पहली कॉल (डिस्पैच) है। यह अप्रत्यक्ष और बहुरूपी एजेंट (`drawing_data_agent') पास करता है, जिससे हमारे विज़िटर कोड को केवल दो चीजों के बारे में जानने की अनुमति मिलती है:

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

चूँकि 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 की 'draw' सुविधा को पूरा करने के लिए TUPLE के नामित तत्वों का उपयोग करती है), जो आवश्यक है और इसे टाला नहीं गया है (और संभवतया नहीं भी किया जा सकता है)।

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

आकार

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

इसके अतिरिक्त, कृपया इस तथ्य पर ध्यान दें कि SHAPE किसी भी ग्राहक को पूरी तरह से निर्यात की गई सुविधा के रूप में केवल 'drawing_data_agent' प्रदान करता है। इसलिए, निर्माण के अतिरिक्त, SHAPE के साथ वार्तालाप करने का एकमात्र विधि 'drawing_data_agent' की सुविधाओं के माध्यम से है, जिसका उपयोग किसी भी क्लाइंट द्वारा 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 ...