2012-01-10 14 views
6

विकिपीडिया sample और गोफ बुक में, विज़िटर पैटर्न का उपयोग कुछ स्वीकार्य पर accept विधि को कॉल करके शुरू किया जाता है। लेकिन यह इस तरह क्यों है? हम visit विधि को वांछित स्वीकार्य के साथ तर्क के रूप में क्यों कॉल नहीं कर सकते? हम अभी भी विज़िटर व्यवहार को 2 प्रकार - आगंतुक और स्वीकार्य (डबल प्रेषण) पर निर्भर कर सकते हैं - और हम अनावश्यक कॉल को खत्म कर सकते हैं (जैसा कि मुझे लगता है)।हम Acceptor.accept() को कॉल करके विज़िटर क्यों शुरू करते हैं, और Visitor.visit() नहीं?

यहाँ इस वर्णन करने के लिए नमूना कोड:

public interface Visitor { 
    void visit(AcceptorA acceptor); 
    void visit(AcceptorB acceptor); 
} 

// 
// Visitor which sings 
// 
class SingingVisitor implements Visitor { 
    public void visit(AcceptorA acceptor) { 
     System.out.println("sing A"); 
    } 

    public void visit(AcceptorB acceptor) { 
     System.out.println("sing B"); 
    } 
} 


// 
// Visitor which talks 
// 
class TalkingVisitor implements Visitor { 
    public void visit(AcceptorA acceptor) { 
     System.out.println("talk A"); 
    } 

    public void visit(AcceptorB acceptor) { 
     System.out.println("talk B"); 
    } 
} 

// 
// Acceptor subclasses 
// 
class AcceptorA implements BaseAcceptor { 
} 

class AcceptorB implements BaseAcceptor { 
} 

// 
// Launcher class 
// 
class VisitorMain { 
    public static void main(String[] args) { 
     Visitor v = new TalkingVisitor(); 
     AcceptorA a = new AcceptorA(); 
     AcceptorB b = new AcceptorB(); 

     v.visit(a); 
     v.visit(b); 
     v = new SingingVisitor(); 
     v.visit(a); 
     v.visit(b); 
    } 
} 
+0

यह सिर्फ एक उदाहरण है - किसी ने भी नहीं कहा कि इसे इस तरह कार्यान्वित किया जाना है। –

+3

@ डेव न्यूटन मैंने इस सवाल से बिल्कुल यह पता लगाने के लिए कहा है कि क्या मैं गोफ दृष्टिकोण का कुछ लाभ देख रहा हूं या यह सिर्फ एक सम्मेलन है। –

उत्तर

5

पर विचार करें:

class House implements HouseAcceptor { 
    HouseAcceptor kitchen; 
    HouseAcceptor livingRoom; 

    void accept(HouseVisitor visitor) { 
     visitor.visit(this); 
     kitchen.accept(visitor); 
     livingRoom.accept(visitor); 
    } 
} 

class Kitchen implements HouseAcceptor { 
    void accept(HouseVisitor visitor) { 
     visitor.visit(this); 
    } 
} 

class LivingRoom implements HouseAcceptor { 
    void accept(HouseVisitor visitor) { 
     visitor.visit(this); 
    } 
} 

class SpeakingHouseVisitor implements HouseVisitor { 
    void visit(HouseAcceptor acceptor) { 
     System.out.println("Inside a HouseAcceptor"); 
    } 

    void visit(House acceptor) { 
     System.out.println("Inside a House"); 
    } 

    void visit(Kitchen acceptor) { 
     System.out.println("Inside a Kitchen"); 
    } 

    void visit(LivingRoom acceptor) { 
     System.out.println("Inside a LivingRoom"); 
    } 
} 

... 
HouseAcceptor acceptor = new House(); 
HouseVisitor visitor = new SpeakingHouseVisitor(); 

... 
// Doing it your way 
visitor.visit(acceptor); 
// Output: Inside a HouseAcceptor 

// Doing it the right way 
acceptor.accept(visitor); 
// Output: 
// Inside a House 
// Inside a Kitchen 
// Inside a LivingRoom 

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

2

Visitor क्योंकि कैसे एक बना Object के निजी आंतरिक क्षेत्रों नेविगेट करने का ज्ञान नहीं है।

यदि आप Visitor.visit(something) कहलाते हैं तो उसे यह पता लगाना होगा कि क्या कुछ निजी क्षेत्र हैं जिन्हें ट्रांसवर्सल की आवश्यकता है। ऐसा करने के लिए, आपको स्वीकार करने के लिए something की आवश्यकता है। एक बार जब आप निर्णय लेते हैं कि नेविगेशन विज़िट ऑब्जेक्ट्स (और Visitor नहीं) में होना चाहिए, तो आपको एहसास हो कि आपको Visitor पर कॉल करने की आवश्यकता है ताकि यह बताने के लिए कि नेविगेशन पथ में अगला तत्व क्या है। आमतौर पर accept(...) विधि है; हालांकि, अगर आपने accept(...) को नेविगेशन (पैरामीटर में प्रतिनिधिमंडल द्वारा) शुरू करने के लिए केवल एक रैपर बनाने का प्रयास किया है, तो आपको Visitor बताए जाने के तरीकों के दूसरे सेट की आवश्यकता है, अब आप एक्स दर्ज कर रहे हैं, अब आपका प्रवेश वाई।

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

अपने सुझाए गए दृष्टिकोण का उपयोग करके, जब किसी ने विज़िट किए गए आइटमों के पदानुक्रम में एक नया प्रकार जोड़ा, तो किसी को सभी आगंतुकों को फिर से कंपाइल करने की आवश्यकता होगी, यहां तक ​​कि आगंतुकों को नए प्रकार में कोई रूचि नहीं थी।

एक अच्छा समझौता होगा:

public interface Visitable { 
    public void accept(Visitor v); 
} 

अपने सभी "डेटा पदानुक्रम" दर्शनीय लागू करता रहे थे, और अपने आगंतुक तो

public abstract class Visitor { 

    public void initiate(Visitable v) { 
    v.accept(this); 
    } 

    public abstract void accept(...); 
    public abstract void accept(...); 
    public abstract void accept(...); 

} 

की तरह एक "सुविधा विधि" है लेकिन यह आप पर निर्भर है यदि इंटरफेस रखना ऐसी बेस क्लास के लिए बेहतर है। मेरे लिए मैं अधिक कमजोर युग्मित इंटरफ़ेस का पक्ष लेगा, लेकिन राय अलग-अलग होती है।

+0

-1 आप विज़िटर/कंपोजिट/इटरेटर कॉम्बो का वर्णन कर रहे हैं। विज़िटर पैटर्न उस से भी उपयोगी है। और आपका समझौता मेरे लिए एक ट्रिपल-प्रेषण जैसा दिखता है आगंतुक-> दृश्यमान-> आगंतुक। – greyfairer

+0

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

+0

यदि आपके पास समग्र विज़िट योग्य नहीं है तो विज़िटर पैटर्न भी उपयोगी होता है। इस मामले में विज़िटेबल आगंतुक को केवल एक बार कॉल करेगा, लेकिन यह सही ओवरलोडेड विधि को कॉल करेगा। – greyfairer

5

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

List<BaseAcceptor> list = ... 
for(BaseAcceptor ba: list) 
    vi.visit(ba) 

जावा संकलक निर्धारित नहीं कर सकता (स्थिर) क्या बा हो जाएगा, तो यह संकलन समय जो कॉल करने के लिए विधि की यात्रा पर तय नहीं कर सकता। आपको एक अतिरिक्त विधि लिखनी होगी:

public void visit(BaseAcceptor ba){ 
    if(ba instanceof AcceptorA) 
    visit((AcceptorA)ba); 
    else if(ba instanceof AcceptorB) 
    visit((AcceptorB)ba); 
} 

विज़िटर पैटर्न का उपयोग करना आवश्यक नहीं है।

+0

यदि मेरे पास 'विज़िटर' इंटरफ़ेस में "स्टब" विज़िट (बेसएसेप्टर) विधि है, तो पहला कोड * संकलित होगा। लेकिन यह अभी भी मुझे वांछित व्यवहार नहीं देगा, यह सही है। –

0

आपके पास कोई डबल प्रेषण नहीं है। स्वीकार करते हैं आमतौर पर एक तर्क के रूप में एक अमूर्त आगंतुक लेता है।

संबंधित मुद्दे