मुझे विज़िटर डिज़ाइन पैटर्न का उपयोग कब करना चाहिए? [बन्द है]


315

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

जैसा कि कोई है जो हाल ही में वास्तव में डेकोरेटर पैटर्न मिला है और अब इसके लिए उपयोग देख रहा है बिल्कुल हर जगह मैं वास्तव में सहज रूप से इस प्रतीत होता है आसान पैटर्न को समझने में सक्षम होना चाहता हूं।


7
दो घंटे तक लॉबी में रुकने के दौरान मेरे ब्लैकबेरी पर इस लेख को पढ़ने के बाद अंत में जेरेमी मिलर द्वारा इसे पढ़ लिया गया । यह लंबा है, लेकिन दोहरे प्रेषण, आगंतुक, और समग्र का एक अद्भुत विवरण देता है, और आप इन के साथ क्या कर सकते हैं।
जॉर्ज मौअर

1
यहाँ एक अच्छा लेख है: codeproject.com/Articles/186185/Visitor-Design-Pattern
Seyed Morteza Mousavi

3
विज़िटर पैटर्न? कौनसा? मुद्दा यह है: इस डिजाइन पैटर्न के आसपास बहुत सी गलतफहमी और शुद्ध भ्रम है। मैंने लिखा है और लेख है जो उम्मीद करता है कि इस अराजकता के लिए कुछ आदेश दिया जाएगा: rgomes-info.blogspot.co.uk/2013/01/…
रिचर्ड गोम्स

जब आप संघ डेटा प्रकारों पर फ़ंक्शन ऑब्जेक्ट रखना चाहते हैं, तो आपको विज़िटर पैटर्न की आवश्यकता होगी। आपको आश्चर्य हो सकता है कि फ़ंक्शन ऑब्जेक्ट और यूनियन डेटा प्रकार क्या हैं, तो यह पढ़ने लायक है ccs.neu.edu/home/matthias/htdc.html
वी किउ

यहाँ और यहाँ उदाहरण ।
jaco0646

जवाबों:


315

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

class Animal {  };
class Dog: public Animal {  };
class Cat: public Animal {  };

(मान लीजिए कि यह एक अच्छी तरह से स्थापित इंटरफ़ेस के साथ एक जटिल पदानुक्रम है।)

अब हम पदानुक्रम में एक नया ऑपरेशन जोड़ना चाहते हैं, अर्थात् हम चाहते हैं कि प्रत्येक जानवर अपनी आवाज़ बनाये। जहां तक ​​पदानुक्रम यह सरल है, आप इसे सीधे बहुरूपता के साथ कर सकते हैं:

class Animal
{ public: virtual void makeSound() = 0; };

class Dog : public Animal
{ public: void makeSound(); };

void Dog::makeSound()
{ std::cout << "woof!\n"; }

class Cat : public Animal
{ public: void makeSound(); };

void Cat::makeSound()
{ std::cout << "meow!\n"; }

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

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

class Operation
{
public:
    virtual void hereIsADog(Dog *d) = 0;
    virtual void hereIsACat(Cat *c) = 0;
};

फिर, हम नए कार्यों को स्वीकार करने के लिए पदानुक्रम को संशोधित करते हैं:

class Animal
{ public: virtual void letsDo(Operation *v) = 0; };

class Dog : public Animal
{ public: void letsDo(Operation *v); };

void Dog::letsDo(Operation *v)
{ v->hereIsADog(this); }

class Cat : public Animal
{ public: void letsDo(Operation *v); };

void Cat::letsDo(Operation *v)
{ v->hereIsACat(this); }

अंत में, हम कैट और डॉग को संशोधित किए बिना वास्तविक ऑपरेशन को लागू करते हैं :

class Sound : public Operation
{
public:
    void hereIsADog(Dog *d);
    void hereIsACat(Cat *c);
};

void Sound::hereIsADog(Dog *d)
{ std::cout << "woof!\n"; }

void Sound::hereIsACat(Cat *c)
{ std::cout << "meow!\n"; }

अब आपके पास पदानुक्रम को संशोधित किए बिना संचालन को जोड़ने का एक तरीका है। यहाँ दिया गया है कि यह कैसे काम करता है:

int main()
{
    Cat c;
    Sound theSound;
    c.letsDo(&theSound);
}

19
S.Lott, एक पेड़ चलना वास्तव में आगंतुक पैटर्न नहीं है। (यह "पदानुक्रमित आगंतुक पैटर्न" है, जो भ्रामक रूप से पूरी तरह से अलग है।) वंशानुक्रम या इंटरफ़ेस कार्यान्वयन का उपयोग किए बिना GOF आगंतुक पैटर्न को दिखाने का कोई तरीका नहीं है।
उदार

14
@Knownasilya - यह सच नहीं है। & -ऑपरेटर साउंड-ऑब्जेक्ट का पता देता है, जिसे इंटरफ़ेस द्वारा आवश्यक है। letsDo(Operation *v) एक सूचक की जरूरत है।
एक्विलापैक्स

3
सिर्फ स्पष्टता के लिए, क्या विज़िटर डिज़ाइन पैटर्न का यह उदाहरण सही है?
गॉडजिला

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

6
अंत में आपके "मुख्य" उदाहरण में: theSound.hereIsACat(c)काम किया होगा, आप पैटर्न द्वारा पेश किए गए सभी ओवरहेड के लिए कैसे उचित हैं? दोहरे प्रेषण का औचित्य है।
franssu

131

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


1) मेरा पसंदीदा उदाहरण "प्रभावी C ++" के प्रशंसित लेखक स्कॉट मेयर्स हैं, जिन्होंने इसे अपने सबसे महत्वपूर्ण C ++ आहा में से एक कहा है ! क्षणों कभी


3
+1 "कोई पैटर्न नहीं है" - सही उत्तर। सबसे उत्तोलित उत्तर साबित करता है कि कई सी ++ प्रोग्रामर को अभी तक एक प्रकार के एनम और स्विच केस (सी तरीका) का उपयोग करके "एडहॉक" बहुरूपता पर आभासी कार्यों की सीमाओं का एहसास करना है। वर्चुअल का उपयोग करने के लिए यह नट और अदृश्य हो सकता है, लेकिन यह अभी भी एकल प्रेषण तक सीमित है। मेरी व्यक्तिगत राय में, यह c ++ का सबसे बड़ा दोष है।
user3125280

@ user3125280 मैंने अब विज़िटर पैटर्न पर 4/5 लेख और डिज़ाइन पैटर्न अध्याय पढ़े हैं, और उनमें से कोई भी इस मामले में किसी स्टमक पर इस अस्पष्ट पैटर्न का उपयोग करने का लाभ नहीं समझाता है, या जब आप एक दूसरे का उपयोग कर सकते हैं। कम से कम इसे ऊपर लाने के लिए Thx!
स्पिंकस

4
@sam मैं बहुत यकीन है कि वे इसे व्याख्या कैसे करते हैं - यह एक ही लाभ है कि आप हमेशा मिलता उपवर्गीकरण / क्रम बहुरूपता से खत्म हो गया switch: switchग्राहक के पक्ष (कोड दोहराव) पर निर्णय लेने की कड़ी मेहनत से कोड और स्थिर प्रकार की जाँच की पेशकश नहीं करता ( पूर्णता और मामलों की विशिष्टता आदि के लिए जाँच करें)। एक आगंतुक पैटर्न प्रकार चेकर द्वारा सत्यापित किया जाता है, और आमतौर पर क्लाइंट कोड को सरल बनाता है।
कोनराड रुडोल्फ

@KonradRudolph उसके लिए धन्यवाद। हालांकि, इसका उल्लेख स्पष्ट रूप से पैटर्न या उदाहरण के लिए विकिपीडिया लेख में नहीं किया गया है। मैं आपसे असहमत नहीं हूं, लेकिन आप तर्क दे सकते हैं कि किसी मामले का उपयोग करने के लाभ भी हैं, इसलिए इसका विचित्र रूप से आम तौर पर विरोधाभासी नहीं है: 1. आपको अपने संग्रह की वस्तुओं पर एक स्वीकार () विधि की आवश्यकता नहीं है। 2. ~ आगंतुक अज्ञात प्रकार की वस्तुओं को संभाल सकता है। इस प्रकार मामला stmt शामिल प्रकार के एक परिवर्तनशील संग्रह के साथ वस्तु संरचनाओं पर काम करने के लिए एक बेहतर फिट लगता है। पैटर्न मानते हैं कि विज़िटर पैटर्न ऐसे परिदृश्य (p333) के अनुकूल नहीं है।
3

1
@SamPinkus कोनराड का स्थान - इसीलिए virtualजैसे आधुनिक प्रोग्रामिंग भाषाओं में विशेषताएं इतनी उपयोगी हैं - वे एक्स्टेंसिबल प्रोग्राम्स के मूल बिल्डिंग ब्लॉक हैं - मेरी राय में c रास्ता (नेस्टेड स्विच या पैटर्न मैच, आदि आपकी पसंद की भाषा पर निर्भर करता है) कोड में सुदूर क्लीनर, जिसे एक्स्टेंसिबल होने की आवश्यकता नहीं है और मैं इस शैली को जटिल सॉफ्टवेयर जैसे प्रोवर 9. में देखकर सुखद आश्चर्यचकित हो गया हूं। इससे भी महत्वपूर्ण बात यह है कि कोई भी भाषा जो एक्स्टेंसिबिलिटी प्रदान करना चाहती है, उसे शायद रीसर्सिव सिंगल डिस्पैच (यानी आगंतुक)।
user3125280

84

यहां हर कोई सही है, लेकिन मुझे लगता है कि यह "जब" को संबोधित करने में विफल रहता है। सबसे पहले, डिजाइन पैटर्न से:

विज़िटर आपको उन तत्वों की कक्षाओं को बदलने के बिना एक नए ऑपरेशन को परिभाषित करने देता है, जिस पर वह संचालित होता है।

अब, चलो एक साधारण वर्ग पदानुक्रम के बारे में सोचते हैं। मेरे पास कक्षा 1, 2, 3 और 4 हैं और विधियां ए, बी, सी और डी हैं। उन्हें एक स्प्रेडशीट की तरह रखें: कक्षाएं रेखाएं हैं और विधियां स्तंभ हैं।

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

कभी-कभी, हालांकि, कक्षाएं अपेक्षाकृत स्थिर होती हैं, लेकिन आपको अक्सर अधिक विधियों को जोड़ने की आवश्यकता होती है - कॉलम जोड़ना। OO डिजाइन में मानक तरीका सभी वर्गों के लिए ऐसे तरीकों को जोड़ना होगा, जो महंगा हो सकता है। विज़िटर पैटर्न इसे आसान बनाता है।

वैसे, यह वह समस्या है जो स्काला के पैटर्न से मेल खाती है।


मैं सिर्फ एक श्रेष्ठता वर्ग पर आगंतुक पैटर्न का उपयोग क्यों करूंगा। मैं अपनी उपयोगिता वर्ग को इस तरह कह सकता हूं: AnalyticsManger.visit (someObjectToVisit) बनाम AnalyticsVisitor.visit (someOjbectToVisit)। क्या फर्क पड़ता है ? वे दोनों चिंता का अधिकार सही करते हैं? आशा है कि आप मदद कर सकते हैं।
j2emanue

@ j2emanue क्योंकि विज़िटर पैटर्न रनटाइम पर सही विज़िटर के अधिभार का उपयोग करता है। जबकि आपके कोड को सही अधिभार को कॉल करने के लिए कास्टिंग की आवश्यकता होती है।
प्रवेश

वहाँ एक दक्षता हासिल है कि साथ है? मुझे लगता है कि यह एक अच्छा विचार कास्टिंग से बचा जाता है
j2emanue

@ j2emanue का विचार कोड लिखना है जो खुले / बंद सिद्धांत के अनुरूप है, प्रदर्शन के कारण नहीं। चाचा बॉब butunclebob.com/ArticleS.Unclebus.PrinciplesOfOod
पहुँच अस्वीकृत

22

आगंतुक डिज़ाइन पैटर्न निर्देशिका के पेड़, एक्सएमएल संरचनाओं, या दस्तावेज़ की रूपरेखा की तरह "पुनरावर्ती" संरचनाओं के लिए वास्तव में अच्छी तरह से काम करता है।

एक आगंतुक वस्तु पुनरावर्ती संरचना में प्रत्येक नोड का दौरा करती है: प्रत्येक निर्देशिका, प्रत्येक XML टैग, जो भी हो। विज़िटर ऑब्जेक्ट संरचना के माध्यम से लूप नहीं करता है। इसके बजाय संरचना के प्रत्येक नोड पर विज़िटर के तरीके लागू होते हैं।

यहाँ एक सामान्य पुनरावर्ती नोड संरचना है। एक निर्देशिका या एक XML टैग हो सकता है। [यदि आपका एक जावा व्यक्ति है, तो बच्चों की सूची बनाने और बनाए रखने के लिए कई अतिरिक्त तरीकों की कल्पना करें।]

class TreeNode( object ):
    def __init__( self, name, *children ):
        self.name= name
        self.children= children
    def visit( self, someVisitor ):
        someVisitor.arrivedAt( self )
        someVisitor.down()
        for c in self.children:
            c.visit( someVisitor )
        someVisitor.up()

visitविधि संरचना में प्रत्येक नोड के लिए एक आगंतुक वस्तु लागू होता है। इस मामले में, यह एक टॉप-डाउन आगंतुक है। आप visitनीचे-ऊपर या कुछ अन्य ऑर्डर करने के लिए विधि की संरचना को बदल सकते हैं ।

यहां आने वालों के लिए सुपरक्लास है। इसका उपयोग visitविधि द्वारा किया जाता है । यह संरचना में प्रत्येक नोड पर "आता है"। चूंकि visitविधि कॉल करती है upऔर down, आगंतुक गहराई का ट्रैक रख सकता है।

class Visitor( object ):
    def __init__( self ):
        self.depth= 0
    def down( self ):
        self.depth += 1
    def up( self ):
        self.depth -= 1
    def arrivedAt( self, aTreeNode ):
        print self.depth, aTreeNode.name

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

यहाँ एक आवेदन है। यह एक वृक्ष संरचना बनाता है, someTree। यह एक बनाता है Visitor, dumpNodes

फिर यह dumpNodesपेड़ पर लागू होता है । dumpNodeवस्तु पेड़ में प्रत्येक नोड "जाएँ" होगा।

someTree= TreeNode( "Top", TreeNode("c1"), TreeNode("c2"), TreeNode("c3") )
dumpNodes= Visitor()
someTree.visit( dumpNodes )

ट्रीनोड visitएल्गोरिथ्म आश्वस्त करेगा कि प्रत्येक ट्रीनोड का उपयोग विज़िटर की arrivedAtविधि के तर्क के रूप में किया जाता है ।


8
जैसा कि दूसरों ने कहा है कि यह "पदानुक्रमित आगंतुक पैटर्न" है।
पीपीसी-कोडर

1
@ पीपीसी-कोडर 'पदानुक्रमित आगंतुक पैटर्न' और आगंतुक पैटर्न के बीच अंतर क्या है?
टिम लवेल-स्मिथ

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

18

इसे देखने का एक तरीका यह है कि विज़िटर पैटर्न आपके ग्राहकों को किसी विशेष वर्ग पदानुक्रम में आपकी सभी कक्षाओं में अतिरिक्त तरीके जोड़ने की सुविधा देने का एक तरीका है।

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

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

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

(मैंने यह सुना है कि विज़िटर पैटर्न अच्छे OO प्रथाओं के साथ संघर्ष में है, क्योंकि यह डेटा के संचालन को डेटा से दूर ले जाता है। विज़िटर पैटर्न ठीक उसी स्थिति में उपयोगी होता है, जब सामान्य OO अभ्यास विफल हो जाते हैं।)


मैं निम्नलिखित पर आपकी राय भी पसंद करूंगा: मैं केवल एक सामान्य वर्ग पर आगंतुक पैटर्न का उपयोग क्यों करूंगा। मैं अपनी उपयोगिता वर्ग को इस तरह कह सकता हूं: AnalyticsManger.visit (someObjectToVisit) बनाम AnalyticsVisitor.visit (someOjbectToVisit)। क्या फर्क पड़ता है ? वे दोनों चिंता का अधिकार सही करते हैं? आशा है कि आप मदद कर सकते हैं।
j2emanue

@ j2emanue: मैं इस सवाल को नहीं समझता। मेरा सुझाव है कि आप इसे मांस दें और किसी को जवाब देने के लिए इसे एक पूर्ण प्रश्न के रूप में पोस्ट करें।
विषम

1
मैंने यहां एक नया प्रश्न पोस्ट किया: stackoverflow.com/questions/52068876/…
j2emanue

14

विज़िटर पैटर्न का उपयोग करने के लिए कम से कम तीन बहुत अच्छे कारण हैं:

  1. कोड का प्रसार कम करें जो डेटा संरचनाओं में परिवर्तन होने पर केवल थोड़ा अलग होता है।

  2. कई डेटा संरचनाओं के लिए एक ही अभिकलन लागू करें, कोड को बदलने के बिना जो गणना को लागू करता है।

  3. विरासत कोड को बदले बिना विरासत पुस्तकालयों में जानकारी जोड़ें।

कृपया इस बारे में लिखे एक लेख पर एक नज़र डालें ।


1
मैंने आपके लेख पर टिप्पणी की है कि मैंने आगंतुक के लिए जो सबसे बड़ा उपयोग किया है। विचार?
जॉर्ज माउर

13

जैसा कि कोनराड रूडोल्फ ने पहले ही बताया, यह उन मामलों के लिए उपयुक्त है, जहां हमें दोहरे प्रेषण की आवश्यकता है

यहां एक ऐसी स्थिति को दिखाने के लिए एक उदाहरण दिया गया है, जहां हमें दोहरे प्रेषण की आवश्यकता है और ऐसा करने में आगंतुक कैसे हमारी मदद करते हैं।

उदाहरण :

आइए बताते हैं कि मेरे पास 3 प्रकार के मोबाइल डिवाइस हैं - आईफोन, एंड्रॉइड, विंडोज मोबाइल।

इन तीनों उपकरणों में एक ब्लूटूथ रेडियो स्थापित है।

मान लेते हैं कि ब्लू टूथ रेडियो 2 अलग-अलग ओईएम - इंटेल और ब्रॉडकॉम से हो सकता है।

बस हमारी चर्चा के लिए उदाहरण को प्रासंगिक बनाने के लिए, यह भी मान लें कि इंटेल रेडियो द्वारा उजागर एपीआई ब्रॉडकॉम रेडियो द्वारा उजागर किए गए लोगों से अलग हैं।

इस तरह मेरी कक्षाएं लगती हैं -

यहां छवि विवरण दर्ज करें यहां छवि विवरण दर्ज करें

अब, मैं एक ऑपरेशन शुरू करना चाहता हूं - मोबाइल डिवाइस पर ब्लूटूथ पर स्विच करना।

इसके फंक्शन सिग्नेचर कुछ इस तरह होने चाहिए -

 void SwitchOnBlueTooth(IMobileDevice mobileDevice, IBlueToothRadio blueToothRadio)

इसलिए डिवाइस के राइट प्रकार और ब्लूटूथ रेडियो के सही प्रकार पर निर्भर करते हुए , इसे उचित चरणों या एल्गोरिथ्म को कॉल करके स्विच किया जा सकता है

प्रिंसिपल में, यह एक 3 x 2 मैट्रिक्स बन जाता है, जहां-जहां मैं सही प्रकार से शामिल वस्तुओं के आधार पर सही संचालन को सदिश करने की कोशिश कर रहा हूं।

दोनों तर्कों के प्रकार के आधार पर बहुरूपी व्यवहार।

यहां छवि विवरण दर्ज करें

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

3x2 मैट्रिक्स के कारण यहां डबल डिस्पैच एक आवश्यकता है

इस प्रकार सेट अप कैसा दिखेगा - यहां छवि विवरण दर्ज करें

मैंने एक और प्रश्न का उत्तर देने के लिए उदाहरण लिखा, कोड और इसकी व्याख्या का उल्लेख यहां किया गया है


9

मुझे निम्नलिखित लिंक्स में यह आसान लगा:

में http://www.remondo.net/visitor-pattern-example-csharp/ मैं एक उदाहरण में पाया गया कि शो एक नकली उदाहरण है कि आगंतुक पैटर्न के लाभ क्या है पता चलता है। यहाँ आप के लिए विभिन्न कंटेनर कक्षाएं हैं Pill:

namespace DesignPatterns
{
    public class BlisterPack
    {
        // Pairs so x2
        public int TabletPairs { get; set; }
    }

    public class Bottle
    {
        // Unsigned
        public uint Items { get; set; }
    }

    public class Jar
    {
        // Signed
        public int Pieces { get; set; }
    }
}

जैसा कि आप ऊपर देख रहे हैं, आपके पास BilsterPackपिल्स के जोड़े हैं , इसलिए आपको जोड़े की संख्या को 2 से गुणा करने की आवश्यकता है। इसके अलावा, आप यह भी देख सकते हैं कि अलग-अलग डेटाटाइप का Bottleउपयोग करें unitऔर डाली जाने की आवश्यकता है।

तो मुख्य विधि में आप निम्नलिखित कोड का उपयोग करके गोली की गणना कर सकते हैं:

foreach (var item in packageList)
{
    if (item.GetType() == typeof (BlisterPack))
    {
        pillCount += ((BlisterPack) item).TabletPairs * 2;
    }
    else if (item.GetType() == typeof (Bottle))
    {
        pillCount += (int) ((Bottle) item).Items;
    }
    else if (item.GetType() == typeof (Jar))
    {
        pillCount += ((Jar) item).Pieces;
    }
}

ध्यान दें कि उपरोक्त कोड उल्लंघन करते हैं Single Responsibility Principle। यदि आप नए प्रकार के कंटेनर जोड़ते हैं, तो आपको मुख्य विधि कोड बदलना होगा। इसके अलावा लंबे समय तक स्विच करना बुरा अभ्यास है।

तो निम्नलिखित कोड शुरू करके:

public class PillCountVisitor : IVisitor
{
    public int Count { get; private set; }

    #region IVisitor Members

    public void Visit(BlisterPack blisterPack)
    {
        Count += blisterPack.TabletPairs * 2;
    }

    public void Visit(Bottle bottle)
    {
        Count += (int)bottle.Items;
    }

    public void Visit(Jar jar)
    {
        Count += jar.Pieces;
    }

    #endregion
}

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

पिलो कंटेनर क्लास में एक्सेप्ट मेथड जोड़कर:

public class BlisterPack : IAcceptor
{
    public int TabletPairs { get; set; }

    #region IAcceptor Members

    public void Accept(IVisitor visitor)
    {
        visitor.Visit(this);
    }

    #endregion
}

हम आगंतुक को गोली कंटेनर कक्षाओं का दौरा करने की अनुमति देते हैं।

अंत में हम निम्नलिखित कोड का उपयोग करके गोली की गणना करते हैं:

var visitor = new PillCountVisitor();

foreach (IAcceptor item in packageList)
{
    item.Accept(visitor);
}

इसका मतलब है: प्रत्येक गोली कंटेनर PillCountVisitorआगंतुक को अपनी गोलियां देखने की अनुमति देता है । वह जानता है कि आपकी गोली कैसे गिननी है।

पर visitor.Countगोलियों के मूल्य है।

में http://butunclebob.com/ArticleS.UncleBob.IuseVisitor आप वास्तविक परिदृश्य है जिसमें आप उपयोग नहीं कर सकते देखने बहुरूपता (उत्तर) एकल जिम्मेदारी सिद्धांत का पालन करें। वास्तव में:

public class HourlyEmployee extends Employee {
  public String reportQtdHoursAndPay() {
    //generate the line for this hourly employee
  }
}

reportQtdHoursAndPayविधि रिपोर्टिंग और प्रतिनिधित्व के लिए है और यह एकल जिम्मेदारी सिद्धांत का उल्लंघन। इसलिए समस्या को दूर करने के लिए विज़िटर पैटर्न का उपयोग करना बेहतर है।


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

8

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

यहाँ पैटर्न का उपयोग करने के कारण हैं:

1) हम हर बार मॉडल को बदलने के बिना नए संचालन को परिभाषित करना चाहते हैं क्योंकि मॉडल अक्सर नहीं बदलता है, अक्सर संचालन में बदलाव होता है।

2) हम मॉडल और व्यवहार को युगल नहीं करना चाहते हैं क्योंकि हम कई अनुप्रयोगों में एक पुन: प्रयोज्य मॉडल रखना चाहते हैं या हम एक एक्स्टेंसिबल मॉडल चाहते हैं जो क्लाइंट कक्षाओं को अपने स्वयं के कक्षाओं के साथ अपने व्यवहार को परिभाषित करने की अनुमति दें।

3) हमारे पास सामान्य ऑपरेशन हैं जो मॉडल के ठोस प्रकार पर निर्भर करते हैं लेकिन हम प्रत्येक उपवर्ग में तर्क को लागू नहीं करना चाहते हैं क्योंकि यह कई वर्गों में और कई स्थानों पर सामान्य तर्क को विस्फोट करेगा

4) हम एक डोमेन मॉडल डिजाइन और एक ही पदानुक्रम के मॉडल वर्गों का उपयोग कर रहे हैं बहुत अधिक विशिष्ट चीजें हैं जो कहीं और इकट्ठा की जा सकती हैं

5) हमें दोहरे प्रेषण की आवश्यकता है
हमारे पास इंटरफ़ेस प्रकारों के साथ घोषित किए गए चर हैं और हम उनका रनटाइम प्रकार के अनुसार उन्हें संसाधित करने में सक्षम होना चाहते हैं… बेशक बिना if (myObj instanceof Foo) {}किसी चाल या उपयोग के ।
विचार इन उदाहरणों को उन तरीकों से पारित करने के लिए है जो एक विशिष्ट प्रसंस्करण को लागू करने के लिए एक ठोस प्रकार के इंटरफ़ेस को पैरामीटर के रूप में घोषित करते हैं। ऐसा करने का यह तरीका संभव नहीं है कि भाषाओं के साथ बॉक्स एकल-प्रेषण पर निर्भर हो क्योंकि रनटाइम पर चुना गया चयन केवल रिसीवर के रनटाइम प्रकार पर निर्भर करता है।
ध्यान दें कि जावा में, कॉल करने का तरीका (हस्ताक्षर) संकलन समय पर चुना जाता है और यह घोषित प्रकार के मापदंडों पर निर्भर करता है, न कि उनके रनटाइम प्रकार पर।

अंतिम बिंदु जो आगंतुक का उपयोग करने का एक कारण है, यह भी एक परिणाम है क्योंकि जैसा कि आप आगंतुक को लागू करते हैं (निश्चित रूप से उन भाषाओं के लिए जो कई प्रेषण का समर्थन नहीं करते हैं), आपको आवश्यक रूप से एक दोहरे प्रेषण कार्यान्वयन की आवश्यकता है।

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


जावा में उदाहरण

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

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

public interface Piece{

    boolean checkMoveValidity(Coordinates coord);

    void performMove(Coordinates coord);

    Piece computeIfKingCheck();

}

प्रत्येक टुकड़ा उपवर्ग इसे इस तरह लागू करेगा:

public class Pawn implements Piece{

    @Override
    public boolean checkMoveValidity(Coordinates coord) {
        ...
    }

    @Override
    public void performMove(Coordinates coord) {
        ...
    }

    @Override
    public Piece computeIfKingCheck() {
        ...
    }

}

और सभी टुकड़ा उपवर्गों के लिए एक ही बात।
यहाँ एक आरेख वर्ग है जो इस डिज़ाइन को दिखाता है:

[मॉडल वर्ग आरेख

यह दृष्टिकोण तीन महत्वपूर्ण कमियां प्रस्तुत करता है:

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

- प्रसंस्करण के रूप में checkMoveValidity()कुछ ऐसा नहीं होना चाहिए जो Pieceउपवर्गों को देख या बदल सकता है।
यह जाँच है कि मानव या कंप्यूटर क्रियाओं से परे है। यह चेक प्रत्येक खिलाड़ी द्वारा अनुरोधित कार्रवाई पर किया जाता है ताकि यह सुनिश्चित हो सके कि अनुरोधित टुकड़ा चाल वैध है।
तो हम भी Pieceइंटरफ़ेस में प्रदान नहीं करना चाहते हैं ।

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

तो चलिए विजिटर पैटर्न का उपयोग करते है!

हमारे पास दो प्रकार की संरचना है:

- मॉडल कक्षाएं जो दौरा किया जाना स्वीकार करती हैं (टुकड़े)

- वे आगंतुक जो उनसे मिलने जाते हैं (ऑपरेशन चला रहे हैं)

यहाँ एक वर्ग आरेख है जो पैटर्न को दिखाता है:

यहां छवि विवरण दर्ज करें

ऊपरी हिस्से में हमारे पास आगंतुक हैं और निचले हिस्से में हमारे पास मॉडल कक्षाएं हैं।

यहाँ PieceMovingVisitorइंटरफ़ेस (प्रत्येक प्रकार के लिए निर्दिष्ट व्यवहार Piece) है:

public interface PieceMovingVisitor {

    void visitPawn(Pawn pawn);

    void visitKing(King king);

    void visitQueen(Queen queen);

    void visitKnight(Knight knight);

    void visitRook(Rook rook);

    void visitBishop(Bishop bishop);

}

टुकड़ा अब परिभाषित किया गया है:

public interface Piece {

    void accept(PieceMovingVisitor pieceVisitor);

    Coordinates getCoordinates();

    void setCoordinates(Coordinates coordinates);

}

इसकी प्रमुख विधि है:

void accept(PieceMovingVisitor pieceVisitor);

यह पहला प्रेषण प्रदान करता है: Pieceरिसीवर के आधार पर एक आह्वान ।
संकलित समय पर, विधि accept()टुकड़ा इंटरफ़ेस की विधि के लिए बाध्य है और रनटाइम पर, बाध्य विधि को रनटाइम Pieceवर्ग पर लागू किया जाएगा ।
और यह accept()एक दूसरा प्रेषण करेगा जो विधि कार्यान्वयन है।

वास्तव में, प्रत्येक Pieceउपवर्ग जो किसी PieceMovingVisitorवस्तु द्वारा जाना चाहता है, वह PieceMovingVisitor.visit()तर्क के रूप में पारित करके विधि को आमंत्रित करता है।
इस तरह, संकलक समय के रूप में जल्द ही संकलित करता है, ठोस प्रकार के साथ घोषित पैरामीटर का प्रकार।
दूसरा प्रेषण है।
यहाँ Bishopउपवर्ग है जो दिखाता है कि:

public class Bishop implements Piece {

    private Coordinates coord;

    public Bishop(Coordinates coord) {
        super(coord);
    }

    @Override
    public void accept(PieceMovingVisitor pieceVisitor) {
        pieceVisitor.visitBishop(this);
    }

    @Override
    public Coordinates getCoordinates() {
        return coordinates;
    }

   @Override
    public void setCoordinates(Coordinates coordinates) {
        this.coordinates = coordinates;
   }

}

और यहाँ एक उपयोग उदाहरण है:

// 1. Player requests a move for a specific piece
Piece piece = selectPiece();
Coordinates coord = selectCoordinates();

// 2. We check with MoveCheckingVisitor that the request is valid
final MoveCheckingVisitor moveCheckingVisitor = new MoveCheckingVisitor(coord);
piece.accept(moveCheckingVisitor);

// 3. If the move is valid, MovePerformingVisitor performs the move
if (moveCheckingVisitor.isValid()) {
    piece.accept(new MovePerformingVisitor(coord));
}

आगंतुक कमियां

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

1) एनकैप्सुलेशन को कम करने / तोड़ने का जोखिम

कुछ प्रकार के ऑपरेशन में, विज़िटर पैटर्न डोमेन ऑब्जेक्ट्स के इनकैप्सुलेशन को कम या तोड़ सकता है।

उदाहरण के लिए, जैसा कि MovePerformingVisitor वर्ग को वास्तविक टुकड़े के निर्देशांक सेट करने की आवश्यकता होती है, Pieceइंटरफ़ेस को ऐसा करने का एक तरीका प्रदान करना होगा:

void setCoordinates(Coordinates coordinates);

की जिम्मेदारी है Pieceनिर्देशांक परिवर्तनों अब Pieceउपवर्गों की तुलना में अन्य वर्गों के लिए खुली है । उपवर्गों
में आगंतुक द्वारा किए गए प्रसंस्करण को आगे Pieceबढ़ाना कोई विकल्प नहीं है।
यह वास्तव में Piece.accept()किसी भी आगंतुक कार्यान्वयन के रूप में एक और मुद्दा बना देगा । यह नहीं पता है कि आगंतुक क्या करता है और इसलिए टुकड़ा स्थिति को बदलने और कैसे करने के बारे में कोई विचार नहीं है।
आगंतुक की पहचान करने का एक तरीका Piece.accept()आगंतुक कार्यान्वयन के अनुसार पोस्ट प्रोसेसिंग करना होगा। यह एक बहुत ही बुरा विचार हो के रूप में यह आगंतुक कार्यान्वयन और टुकड़ा उपवर्गों के बीच और यह शायद के रूप में चाल का उपयोग करने की आवश्यकता होगी के अलावा एक उच्च युग्मन बनाने होगा getClass(), instanceofया किसी मार्कर आगंतुक कार्यान्वयन की पहचान।

2) मॉडल को बदलने की आवश्यकता

Decoratorउदाहरण के लिए कुछ अन्य व्यवहार डिजाइन पैटर्न के विपरीत , आगंतुक पैटर्न घुसपैठ है।
हमें वास्तव में accept()विज़िट किए जाने के लिए एक विधि प्रदान करने के लिए प्रारंभिक रिसीवर वर्ग को संशोधित करने की आवश्यकता है ।
हमारे पास Pieceइसके उपवर्गों के लिए कोई समस्या नहीं थी क्योंकि ये हमारी कक्षाएं हैं
अंतर्निहित या तीसरे पक्ष के वर्गों में, चीजें इतनी आसान नहीं होती हैं।
हमें accept()विधि जोड़ने के लिए उन्हें लपेटना या इनहेरिट करना होगा (यदि हम कर सकते हैं) ।

३) अप्रत्यक्ष

पैटर्न कई गुना अप्रत्यक्ष बनाता है।
दोहरे प्रेषण का अर्थ है एक के बजाय दो आह्वान:

call the visited (piece) -> that calls the visitor (pieceMovingVisitor)

और हम अतिरिक्त अप्रत्यक्ष कर सकते हैं क्योंकि आगंतुक विज़िट की गई वस्तु स्थिति को बदलता है।
यह एक चक्र की तरह लग सकता है:

call the visited (piece) -> that calls the visitor (pieceMovingVisitor) -> that calls the visited (piece)

6

के होर्स्टमैन ने अपने ओओ डिज़ाइन और पैटर्न बुक में विज़िटर को लागू करने के लिए एक महान उदाहरण दिया है । वह समस्या का सारांश प्रस्तुत करता है:

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

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

फ़ाइलसिस्टम वर्ग आरेख

यहां कुछ ऑपरेशन (कार्य) हैं जो हम इस संरचना के साथ लागू करना चाहते हैं:

  • नोड तत्वों के नाम प्रदर्शित करें (एक फ़ाइल लिस्टिंग)
  • नोड तत्वों के आकार की गणना प्रदर्शित करें (जहां एक निर्देशिका के आकार में उसके सभी बाल तत्वों का आकार शामिल है)
  • आदि।

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

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

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

आगंतुक के साथ फाइलसिस्टम वर्ग आरेख लागू किया गया

यह संरचना हमें नई कार्यक्षमताओं को जोड़ने की अनुमति देती है जो संरचना पर ठोस आगंतुकों के रूप में काम करती हैं (संरचना वर्गों को बदले बिना)।

आगंतुक के साथ फाइलसिस्टम वर्ग आरेख लागू किया गया

उदाहरण के लिए, PrintNameVisitorजो निर्देशिका लिस्टिंग कार्यक्षमता को लागू करता है, और PrintSizeVisitorवह आकार के साथ संस्करण को लागू करता है। हम एक दिन एक 'ExportXMLVisitor' की कल्पना कर सकते हैं जो XML में डेटा उत्पन्न करता है, या कोई अन्य आगंतुक जो इसे JSON में उत्पन्न करता है, आदि। हम एक विज़िटर भी कर सकते हैं जो मेरी निर्देशिका ट्री को DOT जैसी चित्रमय भाषा का उपयोग करके प्रदर्शित करता है , कल्पना करने के लिए। एक अन्य कार्यक्रम के साथ।

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


मुझे लगता है कि निर्देशिका संरचना एक अच्छा समग्र पैटर्न है लेकिन आपके अंतिम पैराग्राफ से सहमत है।
ज़ार

5

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


4
जब भी आप आगंतुक का उपयोग करते हैं, तब लगभग हर बार आप किसी वस्तु पदानुक्रम को ट्रेस करने के साथ काम कर रहे होते हैं। एक नेस्टेड ट्री मेनू पर विचार करें। आप सभी नोड्स को संक्षिप्त करना चाहते हैं। यदि आप विज़िटर को लागू नहीं करते हैं तो आपको ग्राफ़ ट्रैवर्सल कोड लिखना होगा। या आगंतुक के साथ rootElement.visit (node) -> node.collapse():। आगंतुक के साथ, प्रत्येक नोड अपने सभी बच्चों के लिए ग्राफ ट्रैवर्सल लागू करता है ताकि आप काम कर सकें।
जॉर्ज मौअर

@GeorgeMauer, दोहरे प्रेषण की अवधारणा ने मेरे लिए प्रेरणा को साफ कर दिया: या तो प्रकार पर निर्भर तर्क प्रकार के साथ है या, दर्द की दुनिया। ट्रैवर्सल लॉजिक वितरित करने का विचार अभी भी मुझे विराम देता है। क्या यह अधिक कुशल है? क्या यह अधिक रखरखाव योग्य है? क्या होगा यदि "एन के स्तर को मोड़ो" को एक आवश्यकता के रूप में जोड़ा जाता है?
nik.shornikov

@ nik.shornikov दक्षता वास्तव में यहाँ एक चिंता का विषय नहीं होना चाहिए। लगभग किसी भी भाषा में, कुछ फ़ंक्शन कॉल नगण्य ओवरहेड हैं। इसके अलावा कुछ भी माइक्रो-ऑप्टिमाइज़ेशन है। क्या यह अधिक रखरखाव योग्य है? अच्छा वह निर्भर करता है। मुझे लगता है कि ज्यादातर बार ऐसा होता है, कभी-कभी ऐसा नहीं होता है। के रूप में "स्तर एन के लिए गुना"। levelsRemainingएक पैरामीटर के रूप में एक काउंटर में आसान पास । अगले स्तर के बच्चों को बुलाने से पहले इसे घटाएं। आपके आगंतुक के अंदर if(levelsRemaining == 0) return
जॉर्ज माउर

1
@GeorgeMauer, दक्षता पर पूरी तरह सहमत मामूली चिंता है। लेकिन स्थिरता, उदाहरण के लिए स्वीकार किए जाते हैं पर हस्ताक्षर, वास्तव में क्या मुझे लगता है कि निर्णय को उबालना चाहिए।
nik.shornikov

5

आस्पेक्ट ऑब्जेक्ट प्रोग्रामिंग के समान भूमिगत कार्यान्वयन के रूप में विज़िटर पैटर्न ।।

उदाहरण के लिए यदि आप उन तत्वों की कक्षाओं को बदले बिना एक नए ऑपरेशन को परिभाषित करते हैं जिस पर वह संचालित होता है


एस्पेक्ट ऑब्जेक्ट प्रोग्रामिंग का उल्लेख करने के लिए ऊपर
मील्मा

5

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

जब आप इसका उपयोग करने पर विचार कर सकते हैं

  1. जब आपके पास कक्षाओं का एक परिवार होता है, तो आप जानते हैं कि उन सभी में कई नए कार्यों को जोड़ना होगा, लेकिन किसी कारण से आप भविष्य में कक्षाओं के परिवार को बदलने या फिर से स्थापित करने में सक्षम नहीं हैं।
  2. जब आप एक नई क्रिया जोड़ना चाहते हैं और उस नई क्रिया को पूरी तरह से एकाधिक कक्षाओं में फैलने के बजाय एक आगंतुक वर्ग के भीतर परिभाषित किया गया है।
  3. जब आपका बॉस कहता है कि आपको कई वर्गों का उत्पादन करना होगा, जो अभी कुछ करना चाहिए ! ... लेकिन वास्तव में किसी को भी नहीं पता है कि अभी कुछ है।

4

मैं इस पैटर्न को तब तक नहीं समझ पाया जब तक कि मैं चाचा बॉब के लेख के साथ नहीं आया और टिप्पणियाँ पढ़ें। निम्नलिखित कोड पर विचार करें:

public class Employee
{
}

public class SalariedEmployee : Employee
{
}

public class HourlyEmployee : Employee
{
}

public class QtdHoursAndPayReport
{
    public void PrintReport()
    {
        var employees = new List<Employee>
        {
            new SalariedEmployee(),
            new HourlyEmployee()
        };
        foreach (Employee e in employees)
        {
            if (e is HourlyEmployee he)
                PrintReportLine(he);
            if (e is SalariedEmployee se)
                PrintReportLine(se);
        }
    }

    public void PrintReportLine(HourlyEmployee he)
    {
        System.Diagnostics.Debug.WriteLine("hours");
    }
    public void PrintReportLine(SalariedEmployee se)
    {
        System.Diagnostics.Debug.WriteLine("fix");
    }
}

class Program
{
    static void Main(string[] args)
    {
        new QtdHoursAndPayReport().PrintReport();
    }
}

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

आगंतुक पैटर्न के साथ आप अपने कोड को क्लीनर बना सकते हैं क्योंकि यह खुले / बंद सिद्धांत का उल्लंघन नहीं करता है और एकल जिम्मेदारी का उल्लंघन नहीं करता है। और यदि आप यात्रा को लागू करना भूल जाते हैं तो यह संकलन नहीं होगा:

public abstract class Employee
{
    public abstract void Accept(EmployeeVisitor v);
}

public class SalariedEmployee : Employee
{
    public override void Accept(EmployeeVisitor v)
    {
        v.Visit(this);
    }
}

public class HourlyEmployee:Employee
{
    public override void Accept(EmployeeVisitor v)
    {
        v.Visit(this);
    }
}

public interface EmployeeVisitor
{
    void Visit(HourlyEmployee he);
    void Visit(SalariedEmployee se);
}

public class QtdHoursAndPayReport : EmployeeVisitor
{
    public void Visit(HourlyEmployee he)
    {
        System.Diagnostics.Debug.WriteLine("hourly");
        // generate the line of the report.
    }
    public void Visit(SalariedEmployee se)
    {
        System.Diagnostics.Debug.WriteLine("fix");
    } // do nothing

    public void PrintReport()
    {
        var employees = new List<Employee>
        {
            new SalariedEmployee(),
            new HourlyEmployee()
        };
        QtdHoursAndPayReport v = new QtdHoursAndPayReport();
        foreach (var emp in employees)
        {
            emp.Accept(v);
        }
    }
}

class Program
{

    public static void Main(string[] args)
    {
        new QtdHoursAndPayReport().PrintReport();
    }       
}  
}

जादू यह है कि जबकि v.Visit(this)यह एक ही है यह वास्तव में अलग दिखता है क्योंकि यह आगंतुक के विभिन्न अधिभार कहता है।


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

3

@Federico A. Ramponi के शानदार जवाब के आधार पर।

जरा सोचिए आपके पास यह पदानुक्रम है:

public interface IAnimal
{
    void DoSound();
}

public class Dog : IAnimal
{
    public void DoSound()
    {
        Console.WriteLine("Woof");
    }
}

public class Cat : IAnimal
{
    public void DoSound(IOperation o)
    {
        Console.WriteLine("Meaw");
    }
}

यदि आपको यहां "वॉक" विधि जोड़ने की आवश्यकता है तो क्या होगा? यह पूरे डिजाइन के लिए दर्दनाक होगा।

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

तो, बस इस C # उदाहरण को जांचें और चलाएं:

using System;
using System.Collections.Generic;

namespace VisitorPattern
{
    class Program
    {
        static void Main(string[] args)
        {
            var animals = new List<IAnimal>
            {
                new Cat(), new Cat(), new Dog(), new Cat(), 
                new Dog(), new Dog(), new Cat(), new Dog()
            };

            foreach (var animal in animals)
            {
                animal.DoOperation(new Walk());
                animal.DoOperation(new Sound());
            }

            Console.ReadLine();
        }
    }

    public interface IOperation
    {
        void PerformOperation(Dog dog);
        void PerformOperation(Cat cat);
    }

    public class Walk : IOperation
    {
        public void PerformOperation(Dog dog)
        {
            Console.WriteLine("Dog walking");
        }

        public void PerformOperation(Cat cat)
        {
            Console.WriteLine("Cat Walking");
        }
    }

    public class Sound : IOperation
    {
        public void PerformOperation(Dog dog)
        {
            Console.WriteLine("Woof");
        }

        public void PerformOperation(Cat cat)
        {
            Console.WriteLine("Meaw");
        }
    }

    public interface IAnimal
    {
        void DoOperation(IOperation o);
    }

    public class Dog : IAnimal
    {
        public void DoOperation(IOperation o)
        {
            o.PerformOperation(this);
        }
    }

    public class Cat : IAnimal
    {
        public void DoOperation(IOperation o)
        {
            o.PerformOperation(this);
        }
    }
}

चलना, खाना दोनों उपयुक्त उदाहरण नहीं हैं क्योंकि वे दोनों के Dogसाथ ही आम हैं Cat। आप उन्हें बेस क्लास में बना सकते हैं ताकि वे विरासत में मिले या उपयुक्त उदाहरण चुनें।
अभिनव गौनियाल

लगता है कि अलग अलग हैं, अच्छा नमूना है, लेकिन अनिश्चित अगर यह आगंतुक पैटर्न के साथ कुछ नहीं करना है
DAG

3

आगंतुक

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

आगंतुक संरचना:

यहां छवि विवरण दर्ज करें

विज़िटर पैटर्न का उपयोग करें यदि:

  1. एक संरचना में समूहीकृत विभिन्न प्रकारों की वस्तुओं पर समान संचालन करना होता है
  2. आपको कई विशिष्ट और असंबंधित कार्यों को निष्पादित करने की आवश्यकता है। यह ऑपरेशन को ऑब्जेक्ट्स स्ट्रक्चर से अलग करता है
  3. ऑब्जेक्ट संरचना में बदलाव के बिना नए संचालन को जोड़ना होगा
  4. संबंधित कक्षाओं को बदलने या निकालने के लिए मजबूर करने के बजाय एक ही वर्ग में संबंधित कार्यों को इकट्ठा करें
  5. वर्ग पुस्तकालयों के लिए फ़ंक्शंस जोड़ें, जिनके लिए आपके पास स्रोत नहीं है या स्रोत को बदल नहीं सकते हैं

भले ही विज़िटर पैटर्न ऑब्जेक्ट में मौजूदा कोड को बदलने के बिना नए ऑपरेशन को जोड़ने के लिए लचीलापन प्रदान करता है, लेकिन यह लचीलापन एक खामी के साथ आया है।

यदि कोई नया दृश्यमान ऑब्जेक्ट जोड़ा गया है, तो उसे विज़िटर और कंक्रीटविज़िटर कक्षाओं में कोड परिवर्तन की आवश्यकता होती है । इस समस्या को हल करने के लिए एक वर्कअराउंड है: प्रतिबिंब का उपयोग करें, जिसका प्रदर्शन पर प्रभाव पड़ेगा।

सांकेतिक टुकड़ा:

import java.util.HashMap;

interface Visitable{
    void accept(Visitor visitor);
}

interface Visitor{
    void logGameStatistics(Chess chess);
    void logGameStatistics(Checkers checkers);
    void logGameStatistics(Ludo ludo);    
}
class GameVisitor implements Visitor{
    public void logGameStatistics(Chess chess){
        System.out.println("Logging Chess statistics: Game Completion duration, number of moves etc..");    
    }
    public void logGameStatistics(Checkers checkers){
        System.out.println("Logging Checkers statistics: Game Completion duration, remaining coins of loser");    
    }
    public void logGameStatistics(Ludo ludo){
        System.out.println("Logging Ludo statistics: Game Completion duration, remaining coins of loser");    
    }
}

abstract class Game{
    // Add game related attributes and methods here
    public Game(){

    }
    public void getNextMove(){};
    public void makeNextMove(){}
    public abstract String getName();
}
class Chess extends Game implements Visitable{
    public String getName(){
        return Chess.class.getName();
    }
    public void accept(Visitor visitor){
        visitor.logGameStatistics(this);
    }
}
class Checkers extends Game implements Visitable{
    public String getName(){
        return Checkers.class.getName();
    }
    public void accept(Visitor visitor){
        visitor.logGameStatistics(this);
    }
}
class Ludo extends Game implements Visitable{
    public String getName(){
        return Ludo.class.getName();
    }
    public void accept(Visitor visitor){
        visitor.logGameStatistics(this);
    }
}

public class VisitorPattern{
    public static void main(String args[]){
        Visitor visitor = new GameVisitor();
        Visitable games[] = { new Chess(),new Checkers(), new Ludo()};
        for (Visitable v : games){
            v.accept(visitor);
        }
    }
}

स्पष्टीकरण:

  1. Visitable( Element) एक इंटरफ़ेस है और इस इंटरफ़ेस विधि को कक्षाओं के एक सेट में जोड़ा जाना है।
  2. Visitorएक इंटरफ़ेस है, जिसमें Visitableतत्वों पर एक ऑपरेशन करने के तरीके शामिल हैं।
  3. GameVisitorएक वर्ग है, जो Visitorइंटरफ़ेस को लागू करता है ( ConcreteVisitor)।
  4. प्रत्येक Visitableतत्व इंटरफ़ेस Visitorकी एक प्रासंगिक विधि को स्वीकार और आमंत्रित Visitorकरता है।
  5. आप का इलाज कर सकते Gameके रूप में Elementऔर ठोस खेल पसंद Chess,Checkers and Ludoके रूप में ConcreteElements

उपरोक्त उदाहरण में, Chess, Checkers and Ludoतीन अलग-अलग खेल (और Visitableकक्षाएं) हैं। एक ठीक दिन पर, मुझे प्रत्येक गेम के आँकड़े लॉग करने के लिए एक परिदृश्य का सामना करना पड़ा। इसलिए सांख्यिकी कार्यक्षमता को लागू करने के लिए व्यक्तिगत वर्ग को संशोधित किए बिना, आप उस जिम्मेदारी को GameVisitorकक्षा में केंद्रीकृत कर सकते हैं , जो प्रत्येक गेम की संरचना को संशोधित किए बिना आपके लिए चाल चलता है।

उत्पादन:

Logging Chess statistics: Game Completion duration, number of moves etc..
Logging Checkers statistics: Game Completion duration, remaining coins of loser
Logging Ludo statistics: Game Completion duration, remaining coins of loser

को देखें

oodesign लेख

स्रोत लेख

अधिक जानकारी के लिए

डेकोरेटर

पैटर्न एक ही वर्ग से अन्य वस्तुओं के व्यवहार को प्रभावित किए बिना व्यवहार को एक व्यक्तिगत वस्तु में या तो सांख्यिकीय या गतिशील रूप से जोड़ने की अनुमति देता है

संबंधित पोस्ट:

IO के लिए डेकोरेटर पैटर्न

डेकोरेटर पैटर्न का उपयोग कब करें?


2

मुझे वास्तव में विवरण और http://python-3-patterns-idioms-test.readthedocs.io/en/latest/Visitor.html से उदाहरण पसंद है ।

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

इस तरह की समस्या को हल करने वाले डिज़ाइन पैटर्न को "विज़िटर" (डिज़ाइन पैटर्न बुक में अंतिम एक) कहा जाता है, और यह अंतिम अनुभाग में दिखाई गई दोहरी प्रेषण योजना पर बनाता है।

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


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

1

जबकि मैं कैसे और कब समझ गया हूं, मैंने कभी क्यों नहीं समझा। यदि यह C ++ जैसी भाषा की पृष्ठभूमि वाले किसी व्यक्ति की मदद करता है, तो आप इसे बहुत ध्यान से पढ़ना चाहते हैं

आलसी के लिए, हम विज़िटर पैटर्न का उपयोग करते हैं क्योंकि "जबकि वर्चुअल फ़ंक्शंस गतिशील रूप से C ++ में भेजे जाते हैं, फ़ंक्शन ओवरलोडिंग सांख्यिकीय रूप से किया जाता है"

या, यह सुनिश्चित करने के लिए कि जब आप स्पेसशिप संदर्भ में एक अपोलोस्पेसक्राफ्ट ऑब्जेक्ट से बंधे होते हैं, तो एक स्पेसशिप संदर्भ में पास होने के लिए एक और रास्ता रख देते हैं।

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

2
आगंतुक पैटर्न में गतिशील प्रेषण का उपयोग पूरी तरह से मुझे हैरान कर देता है। पैटर्न के सुझाए गए उपयोग ब्रांचिंग का वर्णन करते हैं जो संकलन समय पर किया जा सकता है। ये मामले एक फंक्शन टेम्पलेट के साथ बेहतर प्रतीत होंगे।
प्रिक्सोलिटिक

0

@Federico A. Ramponi के भयानक स्पष्टीकरण के लिए धन्यवाद , मैंने इसे जावा संस्करण में बनाया है । आशा है कि यह मददगार हो सकता है।

जैसा कि @ कोनराड रूडोल्फ ने बताया, यह वास्तव में रन-टाइम विधियों को निर्धारित करने के लिए एक साथ दो ठोस उदाहरणों का उपयोग करके एक डबल प्रेषण है

इसलिए वास्तव में ऑपरेशन निष्पादक के लिए एक सामान्य इंटरफ़ेस बनाने की आवश्यकता नहीं है जब तक कि हमारे पास ऑपरेशन इंटरफ़ेस ठीक से परिभाषित न हो।

import static java.lang.System.out;
public class Visitor_2 {
    public static void main(String...args) {
        Hearen hearen = new Hearen();
        FoodImpl food = new FoodImpl();
        hearen.showTheHobby(food);
        Katherine katherine = new Katherine();
        katherine.presentHobby(food);
    }
}

interface Hobby {
    void insert(Hearen hearen);
    void embed(Katherine katherine);
}


class Hearen {
    String name = "Hearen";
    void showTheHobby(Hobby hobby) {
        hobby.insert(this);
    }
}

class Katherine {
    String name = "Katherine";
    void presentHobby(Hobby hobby) {
        hobby.embed(this);
    }
}

class FoodImpl implements Hobby {
    public void insert(Hearen hearen) {
        out.println(hearen.name + " start to eat bread");
    }
    public void embed(Katherine katherine) {
        out.println(katherine.name + " start to eat mango");
    }
}

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

import static java.lang.System.out;
public class Visitor_2 {
    public static void main(String...args) {
        Hearen hearen = new Hearen();
        FoodImpl food = new FoodImpl();
        hearen.showHobby(food);
        Katherine katherine = new Katherine();
        katherine.showHobby(food);
    }
}

interface Hobby {
    void insert(Hearen hearen);
    void insert(Katherine katherine);
}

abstract class Person {
    String name;
    protected Person(String n) {
        this.name = n;
    }
    abstract void showHobby(Hobby hobby);
}

class Hearen extends  Person {
    public Hearen() {
        super("Hearen");
    }
    @Override
    void showHobby(Hobby hobby) {
        hobby.insert(this);
    }
}

class Katherine extends Person {
    public Katherine() {
        super("Katherine");
    }

    @Override
    void showHobby(Hobby hobby) {
        hobby.insert(this);
    }
}

class FoodImpl implements Hobby {
    public void insert(Hearen hearen) {
        out.println(hearen.name + " start to eat bread");
    }
    public void insert(Katherine katherine) {
        out.println(katherine.name + " start to eat mango");
    }
}

0

आपका प्रश्न यह है कि कब जानना है:

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

//psuedo code
    if(payPal) 
    do paypal checkout 
    if(stripe)
    do strip stuff checkout
    if(payoneer)
    do payoneer checkout

अब कल्पना कीजिए कि मेरे पास 10 भुगतान विधियाँ हैं, यह एक प्रकार की बदसूरत है। तो जब आप देखते हैं कि इस तरह के पैटर्न को टटोलने वाला आगंतुक हाथ से बाहर आने वाले सभी को अलग करने के लिए आता है और आप अंत में कुछ इस तरह कॉल करते हैं:

new PaymentCheckoutVistor(paymentType).visit()

आप यहां उदाहरणों की संख्या से इसे लागू करने का तरीका देख सकते हैं, केवल आपको एक usecase दिखा रहा है।

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