विज़िटर पैटर्न में स्वीकार () विधि का क्या मतलब है?


87

कक्षाओं से एल्गोरिदम को डिकूप करने पर बहुत बात होती है। लेकिन, एक बात स्पष्ट नहीं की जाती है।

वे इस तरह आगंतुक का उपयोग करते हैं

abstract class Expr {
  public <T> T accept(Visitor<T> visitor) {visitor.visit(this);}
}

class ExprVisitor extends Visitor{
  public Integer visit(Num num) {
    return num.value;
  }

  public Integer visit(Sum sum) {
    return sum.getLeft().accept(this) + sum.getRight().accept(this);
  }

  public Integer visit(Prod prod) {
    return prod.getLeft().accept(this) * prod.getRight().accept(this);
  }

सीधे विज़िट (तत्व) को कॉल करने के बजाय, विज़िटर तत्व को अपनी विज़िट विधि को कॉल करने के लिए कहता है। यह आगंतुकों के बारे में वर्ग की अनभिज्ञता के घोषित विचार का खंडन करता है।

PS1 कृपया अपने स्वयं के शब्दों के साथ समझाएं या सटीक स्पष्टीकरण दें। क्योंकि दो प्रतिक्रियाओं से मुझे कुछ सामान्य और अनिश्चित का संदर्भ मिला।

PS2 मेरा अनुमान है: चूंकि getLeft()मूल रिटर्न देता है Expression, इसलिए कॉलिंग में visit(getLeft())परिणाम होगा visit(Expression), जबकि getLeft()कॉलिंग के visit(this)परिणामस्वरूप एक और, अधिक उपयुक्त, यात्रा आह्वान होगा। तो, accept()प्रकार रूपांतरण (उर्फ कास्टिंग) करता है।

PS3 स्काला के पैटर्न का मिलान = स्टेरॉयड पर विज़िटर पैटर्न दिखाता है कि विज़िटर पैटर्न स्वीकार पद्धति के बिना कितना सरल है। विकिपीडिया इस कथन में जोड़ता है : "दिखावा के accept()उपलब्ध होने पर यह विधियाँ अनावश्यक हैं, जो तकनीक के लिए लिंक से जुड़ती है; तकनीक के लिए 'वॉकआउट' शब्द का परिचय देती है।"



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

जवाबों:


154

सी-लाइक लैंग्वेज '(C #, Java, आदि) शब्दार्थ के कारण विज़िटर पैटर्न visit/ acceptकंस्ट्रक्शन एक आवश्यक बुराई है। विज़िटर पैटर्न का लक्ष्य आपकी कॉल को रूट करने के लिए दोहरे-प्रेषण का उपयोग करना है जैसा कि आप कोड को पढ़ने से उम्मीद करेंगे।

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

Node root = GetTreeRoot();
new MyVisitor().visit(root);

यहाँ समस्या है। यदि हमारी MyVisitorकक्षा को निम्नलिखित की तरह परिभाषित किया गया था:

class MyVisitor implements IVisitor {
  void visit(CarNode node);
  void visit(TrainNode node);
  void visit(PlaneNode node);
  void visit(Node node);
}

यदि, रनटाइम पर, वास्तविक प्रकार की परवाह किए बिना root, हमारी कॉल ओवरलोड में जाएगीvisit(Node node) । यह सभी प्रकार के घोषित चर के लिए सही होगा Node। ऐसा क्यों है? क्योंकि जावा और अन्य सी-जैसी भाषाएं केवल स्थैतिक प्रकार , या उस प्रकार को मानती हैं , जिसे चर के रूप में घोषित किया जाता है, जो तय करते समय कॉल करने के लिए अधिभार होता है। जावा, प्रत्येक विधि कॉल के लिए, रनटाइम पर, पूछने के लिए अतिरिक्त कदम नहीं उठाता है, "ठीक है, गतिशील प्रकार क्या है root, मैं देख रहा हूं। यह एक है TrainNode। आइए देखें कि क्या कोई तरीका है MyVisitorजिसमें प्रकार के एक पैरामीटर को स्वीकार करता है।TrainNode... "। कंपाइलर, संकलन-समय पर, निर्धारित करता है कि कौन सी विधि है जिसे कहा जाएगा। (यदि जावा ने वास्तव में तर्कों के गतिशील प्रकारों का निरीक्षण किया, तो प्रदर्शन बहुत भयानक होगा।"

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

विज़िटर पैटर्न का लक्ष्य डबल-प्रेषण को पूरा करना है - न केवल कॉल लक्ष्य का प्रकार माना जाता है ( MyVisitor, आभासी विधियों के माध्यम से), लेकिन यह भी पैरामीटर का प्रकार ( Nodeहम किस प्रकार देख रहे हैं)? आगंतुक पैटर्न हमें यह करने की अनुमति देता हैvisit / acceptसंयोजन ।

इसे हमारी लाइन बदलकर:

root.accept(new MyVisitor());

हम जो चाहते हैं वह प्राप्त कर सकते हैं: आभासी विधि प्रेषण के माध्यम से, हम सही स्वीकार () कॉल को उपवर्ग द्वारा लागू किए गए तरीके से दर्ज करते हैं - हमारे उदाहरण में TrainElement, हम इसका TrainElementकार्यान्वयन करेंगे accept():

class TrainNode extends Node implements IVisitable {
  void accept(IVisitor v) {
    v.visit(this);
  }
}

क्या के दायरे के अंदर इस बिंदु पर संकलक पता है, TrainNodeकी accept? यह जानता है कि स्थिर प्रकार thisएक हैTrainNode । यह जानकारी का एक महत्वपूर्ण अतिरिक्त टुकड़ा है कि संकलक को हमारे कॉलर के दायरे के बारे में पता नहीं था: वहां, इसके बारे rootमें सभी जानते थे कि यह एक था Node। अब संकलक जानता है कि this( root) सिर्फ एक नहीं है Node, लेकिन यह वास्तव में एक है TrainNode। परिणाम में, एक पंक्ति के अंदर पाया accept():v.visit(this) अर्थ पूरी तरह से कुछ और है। संकलक अब एक अधिभार के लिए देखो की होगी visit()कि एक लेता है TrainNode। यदि यह एक नहीं मिल रहा है, तो यह कॉल को एक अधिभार के लिए संकलित करेगा जो एक लेता हैNode। यदि न तो मौजूद है, तो आपको एक संकलन त्रुटि मिलेगी (जब तक कि आपके पास एक अधिभार नहीं होता है object)। निष्पादन इस प्रकार दर्ज करेगा जो हमने सभी के साथ किया था: MyVisitorका कार्यान्वयन visit(TrainNode e)। किसी भी जाति की आवश्यकता नहीं थी, और, सबसे महत्वपूर्ण बात, कोई प्रतिबिंब की आवश्यकता नहीं थी। इस प्रकार, इस तंत्र का ओवरहेड कम है: इसमें केवल सूचक संदर्भ शामिल हैं और कुछ नहीं।

आप अपने प्रश्न में सही हैं - हम कलाकारों का उपयोग कर सकते हैं और सही व्यवहार प्राप्त कर सकते हैं। हालाँकि, अक्सर, हम यह भी नहीं जानते कि नोड किस प्रकार का है। निम्नलिखित पदानुक्रम का मामला लें:

abstract class Node { ... }
abstract class BinaryNode extends Node { Node left, right; }
abstract class AdditionNode extends BinaryNode { }
abstract class MultiplicationNode extends BinaryNode { }
abstract class LiteralNode { int value; }

और हम एक सरल संकलक लिख रहे थे जो एक स्रोत फ़ाइल को पार्स करता है और एक वस्तु पदानुक्रम का उत्पादन करता है जो ऊपर विनिर्देश के अनुरूप है। यदि हम एक आगंतुक के रूप में लागू पदानुक्रम के लिए दुभाषिया लिख ​​रहे थे:

class Interpreter implements IVisitor<int> {
  int visit(AdditionNode n) {
    int left = n.left.accept(this);
    int right = n.right.accept(this); 
    return left + right;
  }
  int visit(MultiplicationNode n) {
    int left = n.left.accept(this);
    int right = n.right.accept(this);
    return left * right;
  }
  int visit(LiteralNode n) {
    return n.value;
  }
}

कास्टिंग हमें बहुत दूर नहीं होगा, क्योंकि हम के प्रकार पता नहीं है leftया rightमें visit()तरीकों। हमारे पार्सर सबसे अधिक संभावना सिर्फ एक प्रकार की वस्तु लौटाएंगेNode जो पदानुक्रम की जड़ के रूप में अच्छी तरह से इंगित करता है, इसलिए हम उस सुरक्षित रूप से भी नहीं डाल सकते हैं। तो हमारे सरल दुभाषिया जैसे दिख सकते हैं:

Node program = parse(args[0]);
int result = program.accept(new Interpreter());
System.out.println("Output: " + result);

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

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


5
प्रभावी जावा आइटम # 41 में यह चेतावनी शामिल है: " उन परिस्थितियों से बचें जहां मापदंडों का एक ही सेट अलग-अलग ओवरलोडिंग में डाले जा सकते हैं। " accept()विधि आवश्यक हो जाती है जब आगंतुक में इस चेतावनी का उल्लंघन किया जाता है।
jaco0646

" आम तौर पर जब विज़िटर पैटर्न का उपयोग किया जाता है, तो एक ऑब्जेक्ट पदानुक्रम शामिल होता है जहां सभी नोड्स एक आधार नोड प्रकार से प्राप्त होते हैं " सी ++ में यह बिल्कुल आवश्यक नहीं है। Boost.Variant, Eggs.Variant
जीन-माइकेल सेलेरियर

मुझे ऐसा लगता है कि जावा में हमें वास्तव में स्वीकार करने की विधि की आवश्यकता नहीं है क्योंकि जावा में हम हमेशा सबसे विशिष्ट प्रकार की विधि कहते हैं
Gilad Baruchian

1
वाह, यह एक भयानक स्पष्टीकरण था। यह देखते हुए कि पैटर्न की सभी छाया संकलक सीमाओं के कारण हैं, और अब आपके लिए स्पष्ट धन्यवाद प्रकट करता है।
अल्फोंसो निशिकावा

@ गिलाडबरुचियन, कंपाइलर सबसे विशिष्ट प्रकार की विधि के लिए एक कॉल उत्पन्न करता है जिसे कंपाइलर निर्धारित कर सकता है।
mmw

15

बेशक यह मूर्खतापूर्ण होगा यदि वह एकमात्र तरीका था जिसे स्वीकार किया जाता है।

लेकिन यह नहीं है।

उदाहरण के लिए, पदानुक्रमों से निपटने के दौरान आगंतुक वास्तव में उपयोगी होते हैं, इस मामले में गैर-टर्मिनल नोड का कार्यान्वयन कुछ इस तरह से हो सकता है

interface IAcceptVisitor<T> {
  void Accept(IVisit<T> visitor);
}
class HierarchyNode : IAcceptVisitor<HierarchyNode> {
  public void Accept(IVisit<T> visitor) {
    visitor.visit(this);
    foreach(var n in this.children)
      n.Accept(visitor);
  }

  private IEnumerable<HierarchyNode> children;
  ....
}

आप समझ सकते हैं? क्या आप बेवकूफ के रूप में वर्णन करते हैं पदानुक्रम का पता लगाने के लिए समाधान है।

यहाँ बहुत लंबा और गहरा लेख है जिससे मुझे आगंतुक समझ में आया

संपादित करें: स्पष्ट करने के लिए: आगंतुक की Visitविधि में नोड पर लागू होने वाले तर्क शामिल हैं। नोड की Acceptविधि में समीपवर्ती नोड पर नेविगेट करने के लिए तर्क शामिल हैं। जिस मामले में आप केवल डबल प्रेषण करते हैं वह एक विशेष मामला है जहां नेविगेट करने के लिए बस आसन्न नोड्स नहीं हैं।


7
आपकी व्याख्या यह नहीं बताती है कि बच्चों को पुनरावृत्त करने के लिए विज़िटर की उचित यात्रा () पद्धति के बजाय नोड की जिम्मेदारी क्यों होनी चाहिए? क्या आपका मतलब है कि मुख्य विचार पदानुक्रम ट्रैवर्सल कोड को साझा कर रहा है जब हमें अलग-अलग आगंतुकों के लिए समान विज़िटिंग पेटेंट की आवश्यकता है? मुझे अनुशंसित पेपर से कोई संकेत नहीं दिखता है।
वैल

1
यह कहना कि नियमित ट्रैवर्सल के लिए स्वीकार करना अच्छा है और सामान्य आबादी के लिए उचित है। लेकिन, मैंने अपना उदाहरण किसी के "मैं जब तक andymaleh.blogspot.com/2008/04/… " पढ़ा, विज़िटर पैटर्न को नहीं समझ सका । न तो इस उदाहरण और न ही विकिपीडिया और न ही अन्य उत्तरों में नेविगेशन लाभ का उल्लेख है। फिर भी, वे सभी इस मूर्खतापूर्ण स्वीकार () की मांग करते हैं। इसीलिए मेरा प्रश्न पूछें: क्यों?
वल

1
@ वाल - आपका क्या मतलब है? मुझे यकीन नहीं है कि आप क्या पूछ रहे हैं। मैं अन्य लेखों के लिए नहीं बोल सकता क्योंकि उन लोगों के पास इस सामान पर अलग-अलग दृष्टिकोण हैं लेकिन मुझे संदेह है कि हम असहमति में हैं। सामान्य तौर पर गणना में बहुत सी समस्याओं को नेटवर्क पर मैप किया जा सकता है, इसलिए उपयोग में सतह के ग्राफ़ के साथ कुछ भी नहीं हो सकता है लेकिन वास्तव में एक समान समस्या है।
जॉर्ज मौएर

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

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

0

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

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

विज़िटर पैटर्न उस समस्या से बचने के लिए कम से कम तीन दृष्टिकोण प्रदान करता है:

  1. यह एक रिकॉर्ड को लॉक कर सकता है, आपूर्ति किए गए फ़ंक्शन को कॉल कर सकता है, और फिर रिकॉर्ड को अनलॉक कर सकता है; रिकॉर्ड को हमेशा के लिए लॉक किया जा सकता है यदि आपूर्ति की गई फ़ंक्शन एक अंतहीन लूप में आती है, लेकिन यदि आपूर्ति की गई फ़ंक्शन रिटर्न देता है या एक अपवाद फेंकता है, तो रिकॉर्ड अनलॉक हो जाएगा (फ़ंक्शन को अपवाद को रिकॉर्ड करने के लिए अमान्य चिह्नित करना उचित हो सकता है; यह बंद है शायद एक अच्छा विचार नहीं है)। ध्यान दें कि यह महत्वपूर्ण है कि अगर कहा जाता है कि फ़ंक्शन अन्य तालों को प्राप्त करने का प्रयास करता है, तो गतिरोध का परिणाम हो सकता है।
  2. कुछ प्लेटफार्मों पर, यह स्ट्रिंग को पकड़े हुए स्थान को 'रेफरी' पैरामीटर के रूप में पास कर सकता है। यह फ़ंक्शन तब स्ट्रिंग की प्रतिलिपि बना सकता है, कॉपी किए गए स्ट्रिंग के आधार पर एक नए स्ट्रिंग की गणना कर सकता है, पुराने स्ट्रिंग को नए से तुलना करने का प्रयास करें, और अगर तुलना बहिष्कृत हो तो पूरी प्रक्रिया को दोहराएं।
  3. यह स्ट्रिंग की एक प्रतिलिपि बना सकता है, स्ट्रिंग पर आपूर्ति की गई फ़ंक्शन को कॉल कर सकता है, फिर मूल को अपडेट करने का प्रयास करने के लिए खुद को ComparExchange का उपयोग करें, और अगर ComparExchange विफल हो, तो पूरी प्रक्रिया को दोहराएं।

विज़िटर पैटर्न के बिना, परमाणु अद्यतनों को निष्पादित करने के लिए लॉक को उजागर करना और विफलता को रोकना होगा यदि कॉलिंग सॉफ़्टवेयर एक सख्त लॉकिंग / अनलॉक प्रोटोकॉल का पालन करने में विफल रहता है। विज़िटर पैटर्न के साथ, परमाणु अद्यतन अपेक्षाकृत सुरक्षित रूप से किए जा सकते हैं।


2
1. दौरा करने का तात्पर्य है कि आपके पास केवल विज़िट किए गए सार्वजनिक तरीकों तक पहुंच है, ताकि विज़िटर के लिए उपयोगी होने के लिए जनता के लिए आंतरिक ताले को सुलभ बनाने की आवश्यकता हो। 2 / कोई भी उदाहरण मैंने पहले से नहीं देखा है कि विज़िटर को विज़िट की स्थिति बदलने के लिए उपयोग किया जाना चाहिए। 3. "पारंपरिक विज़िटरपैटर्न के साथ, कोई केवल तभी निर्धारित कर सकता है जब हम एक नोड में प्रवेश कर रहे हैं। हम नहीं जानते कि क्या हमने वर्तमान नोड में प्रवेश करने से पहले पिछले नोड को छोड़ दिया है।" आप यात्रा के बजाय केवल यात्रा के साथ कैसे अनलॉक करते हैं? अंत में, मैंने आगंतुक के बजाय accpet () के अनुप्रयोगों के बारे में पूछा।
वैल

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

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

0

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

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


-1

अच्छा उदाहरण स्रोत कोड संकलन में है:

interface CompilingVisitor {
   build(SourceFile source);
}

ग्राहक ए लागू कर सकते हैं JavaBuilder , RubyBuilder,XMLValidator , आदि और एक परियोजना में एकत्र करने और सभी स्रोत फ़ाइलों में आने के लिए कार्यान्वयन परिवर्तन की जरूरत नहीं है।

यह एक होगा यदि आपके पास प्रत्येक स्रोत फ़ाइल प्रकार के लिए अलग कक्षाएं हैं तो बुरा पैटर्न होगा:

interface CompilingVisitor {
   build(JavaSourceFile source);
   build(RubySourceFile source);
   build(XMLSourceFile source);
}

यह संदर्भ के लिए नीचे आता है और सिस्टम के किन हिस्सों को आप एक्स्टेंसिबल करना चाहते हैं।


विडंबना यह है कि VisitorPattern हमें खराब पैटर्न का उपयोग करने की पेशकश करता है। यह कहता है कि हमें हर प्रकार के नोड के लिए एक विज़िट विधि को परिभाषित करना होगा जो यह यात्रा करने वाला है। दूसरे, यह स्पष्ट नहीं है कि आपके उदाहरण अच्छे हैं या बुरे? वे मेरे प्रश्न से कैसे संबंधित हैं?
वैल
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.