डबल डिस्पैच

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

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

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

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

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

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

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

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

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

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

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

ऊपर वर्णित समस्या को दोहरे प्रेषण का अनुकरण करके हल किया जा सकता है, उदाहरण के लिए आगंतुक पैटर्न का उपयोग करके। मान लीजिए कि मौजूदा कोड को बढ़ाया गया है ताकि दोनों  और   फ़ंक्शन दिया गया है फिर, जबकि पिछला उदाहरण अभी भी सही ढंग से काम नहीं करता है, कॉल को फिर से फ्रेम करना ताकि अंतरिक्ष यान एजेंट हो, हमें वांछित व्यवहार देता है: यह प्रिंट कर लेता है  और , आशा के अनुसार। कुंजी वह है   रन टाइम पर निम्न कार्य करता है:
 * 1)   एक संदर्भ है, इसलिए C++ vtable में सही विधि ढूंढता है। इस मामले में, यह कॉल करेगा.
 * 2) अंदर ,   एक संदर्भ है, इसलिए   इसके परिणामस्वरूप एक और वेटेबल लुकअप होगा। इस मामले में,   एक का संदर्भ है   इसलिए   बुलाया जाएगा।

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

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

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

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

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

चित्रित करो ETCHASKETCH पर बहुभुज चित्रित करो भित्तिचित्र दीवार पर बहुभुज बनाएं  ETCHASKETCH पर आयत चित्रित करो भित्तिचित्र दीवार पर आयत बनाएं  ETCHASKETCH पर चतुर्भुज चित्रित करो भित्तिचित्र दीवार पर चतुर्भुज एक चित्र बनाएं  ETCHASKETCH पर समांतर चतुर्भुज चित्रित करो भित्तिचित्र दीवार पर समांतर चतुर्भुज बनाएं  ETCHASKETCH पर बहुभुज चित्रित करो भित्तिचित्र दीवार पर बहुभुज बनाएं  ETCHASKETCH पर आयत चित्रित करो भित्तिचित्र दीवार पर आयत

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

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

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

 निर्माण -- सतहों पर आकृतियाँ मुद्रित करें। स्थानीय l_आकार: ARRAYED_LIST [आकार] l_सतहें: ARRAYED_LIST [सतह] करना l_shapes.बनाएँ (6) l_shapes.extend ({POLYGON} बनाएं.make_with_color (लाल)) l_shapes.extend ({RECTANGLE} बनाएं.make_with_color (ग्रे)) l_shapes.extend (बनाएँ {QUADRILATERAL}.make_with_color (हरा)) l_shapes.extend ({PARALLELOGRAM} बनाएं.make_with_color (नीला)) l_shapes.extend (बनाएँ {POLYGON}.make_with_color (पीला)) l_shapes.extend ({RECTANGLE} बनाएं.make_with_color (बैंगनी))

l_surfaces.make बनाएँ (2) l_surfaces.extend (बनाएं {ETCHASKETCH} बनाएं) l_surfaces.extend ({GRAFFITI_WALL} बनाएं। बनाएं)

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

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


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

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

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

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

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

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

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

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

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

आगंतुक
क्लासिक स्पेसशिप उदाहरण के लिए विज़िटर के पास एक डबल-डिस्पैच तंत्र भी है।  निर्माण - स्पेसशिप वस्तुओं को ब्रह्मांड में घूमने और घूमने की अनुमति दें। स्थानीय l_ब्रह्मांड: ARRAYED_LIST [SPACE_OBJECT] एल_उद्यम, l_एक्सेलसियर: अंतरिक्ष यान करना l_enterprise.make_with_name बनाएं (एंटरप्राइज़, A-001) l_excelsior.make_with_name बनाएं (एक्सेलसियर, A-003) एल_यूनिवर्स बनाएं। बनाएं (0) l_univers.force (l_enterprise) l_univers.force (बनाएँ {ASTEROID}.make_with_name (दुष्ट 1, A-002)) l_univers.force (बनाएँ {ASTEROID}.make_with_name (दुष्ट 2, A-003)) l_univers.force (l_excelsior) l_univers.force (बनाएँ {ASTEROID}.make_with_name (दुष्ट 3, A-004)) l_univers.force (बनाएं {SPACESTATION}.make_with_name (डीप स्पेस 9, ए-005)) (l_enterprise, l_univers) पर जाएँ l_enterprise.set_position (A-002) (l_enterprise, l_univers) पर जाएँ l_enterprise.set_position (A-003) (l_enterprise, l_univers) पर जाएँ l_enterprise.set_position (A-004) l_excelsior.set_position (A-005) (l_enterprise, l_univers) पर जाएँ (l_excelsior, l_univers) पर जाएँ l_enterprise.set_position (A-005) (l_enterprise, l_univers) पर जाएँ अंत सुविधा {कोई नहीं}--कार्यान्वयन (a_object: SPACE_OBJECT; a_univers: ARRAYED_LIST [SPACE_OBJECT]) पर जाएँ -- `एक_वस्तु' `एक_ब्रह्मांड' का दौरा करती है। करना a_univers में ic_univers लूप के रूप में फिर संलग्न {SPACE_OBJECT} ic_univers.item को al_univers_object के रूप में जांचें a_object.encounter_agent.call ([al_univers_object.sensor_data_agent]) अंत अंत अंत  डबल-डिस्पैच को पंक्ति #35 में देखा जा सकता है, जहां दो अप्रत्यक्ष एजेंट एक-दूसरे के साथ पूर्ण बहुरूपी संगीत कार्यक्रम में काम करते हुए दो सह-संस्करण कॉल प्रदान करने के लिए मिलकर काम कर रहे हैं। `विज़िट' फ़ीचर के `ए_ऑब्जेक्ट' में एक `एनकाउंटर_एजेंट' होता है जिसे 'अल_यूनिवर्स_ऑब्जेक्ट' से आने वाले `सेंसर_डेटा_एजेंट' के सेंसर डेटा के साथ बुलाया जाता है। इस विशेष उदाहरण का अन्य दिलचस्प हिस्सा 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 को उसके वंशजों के किसी विशिष्ट ज्ञान के साथ प्रोग्राम नहीं किया गया है। SPACE_OBJECT के तीन वंशज वर्ग हैं: हमारे उदाहरण में, क्षुद्रग्रह वर्ग का उपयोग 'दुष्ट' वस्तुओं के लिए किया जाता है, दो सितारा जहाजों के लिए स्पेसशिप और डीप स्पेस नाइन के लिए स्पेसस्टेशन का उपयोग किया जाता है। प्रत्येक वर्ग में, एकमात्र विशेषज्ञता 'प्रकार' सुविधा और वस्तु के कुछ गुणों की सेटिंग है। रचना क्रम में 'नाम' के साथ-साथ 'पद' भी दिया जाता है। उदाहरण के लिए: नीचे स्पेसशिप उदाहरण है। तो, हमारे ब्रह्मांड में कोई भी अंतरिक्ष यान डॉक-सक्षम, मानवयुक्त और गतिशीलता योग्य है। अन्य वस्तुएं, जैसे क्षुद्रग्रह, इनमें से कोई भी चीज़ नहीं हैं। दूसरी ओर, एक स्पेसस्टेशन डॉक-सक्षम और मानवयुक्त दोनों है, लेकिन गतिशीलता योग्य नहीं है। इस प्रकार, जब एक वस्तु का दूसरे के साथ सामना होता है, तो यह पहले यह देखने के लिए जांच करता है कि क्या स्थिति उन्हें एक-दूसरे के आसपास रखती है और यदि वे हैं, तो वस्तुएं अपने मूल गुणों के आधार पर बातचीत करती हैं। ध्यान दें कि समान प्रकार और नाम वाली वस्तुओं को एक ही वस्तु माना जाता है, इसलिए तार्किक रूप से बातचीत की अनुमति नहीं है।

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

यह भी देखें

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