2012-04-12 43 views
23

因此,我已经阅读了关于访问者模式的所有文档,而且我仍然非常困惑。我从另一个SO问题中看到了这个例子,有人能帮助我理解吗?例如,我们何时使用访客设计模式?我想我可能已经理解了其中的一些,但我只是无法看到更大的图景。我怎么知道我什么时候可以使用它?访客模式说明

class equipmentVisited 
{ 
    virtual void accept(equipmentVisitor* visitor) = 0; 
} 

class floppyDisk : public equipmentVisited 
{ 
    virtual void accept(equipmentVisitor* visitor); 
} 

class processor : public equipmentVisited 
{ 
    virtual void accept(equipmentVisitor* visitor); 
} 

class computer : public equipmentVisited 
{ 
    virtual void accept(equipmentVisitor* visitor); 
} 

class equipmentVisitor 
{ 
    virtual void visitFloppyDisk(floppyDisk*); 
    virtual void visitProcessor(processor*); 
    virtual void visitComputer(computer*); 
} 

// Some additional classes inheriting from equipmentVisitor would be here 

equipmentVisited* visited; 
equipmentVisitor* visitor; 

// Here you initialise visited and visitor in any convenient way 

visited->accept(visitor); 
+1

一个完整的示例,请参见Java示例中http://en.wikipedia.org/wiki/Visitor_pattern#Java_example – stefaanv 2012-04-12 07:41:40

+0

在那个例子中,你可以很容易地使equipmentVisitor只使用功能的访问,有三个重载函数每个采用不同的派生的equipemtVisited类作为参数。只有我会让这个班级成为设备访问者。 – 2015-12-30 05:54:19

回答

25

访客模式用于实现double dispatch。简而言之,这意味着执行的代码取决于两个对象的运行时类型。

当您调用常规的虚拟函数时,它是一次调度:执行的代码块取决于单个对象的运行时类型,即您调用的虚拟方法的运行时类型。

随着访问者模式,正被称为最终取决于两个对象的类型的方法 - 实施equipmentVisitor的对象的类型,并且在其上调用accept的对象的类型(即,equipmentVisited子类) 。

还有其他方法可以在C++中实现双重调度。 Scott Meyer的"More Effective C++"的第31项对这个问题进行了深入的探讨。

+1

因此,它像设备的任何子类一样可以调用任何设备子类的“接受”函数?那是对的吗? – 2012-04-12 01:17:18

+0

@dasblinkenlight - 我从来没有“得到”访客模式!仍然与它斗争。所以,请回答这个问题:我有一个从客户类继承的主类。这样,师父可以做所有客户可以做的事情,再加上更多的东西。如果我想要多态使用这些类,那么我需要一个通用的基类。问题:现在我的主类继承了两个类。你认为访问者模式可以解决我的问题吗? – Patricia 2015-03-05 21:45:37

+0

@Patricia为什么你需要一个通用的基类?为什么你不能只使用'Client'作为基指针/引用类型?多态性适用于任何形式的继承,而不仅限于受限制的“接口”模式。 'Master'从'Client'继承,因此可以使用指针或引用''Client'来调用'Master'上的'virtual'方法。认为你需要在混合中添加另一个基类是错误的,并且会对效率产生重大影响。那就是问题所在。 – 2016-08-07 14:50:20

13

我认为模式访客的名字是相当不幸的。 而不是单词访问者,我会说Functor或Operator,而不是'访问'我会说'适用'。

我的访问者模式的理解如下:

在模板元编程(STL/BOOST)(编译时绑定),您可以从结构实现(正交设计) 操作的分离,由的函数对象手段(函子) 例如在

template <class RandomAccessIterator, class Compare> 
void sort (RandomAccessIterator first, RandomAccessIterator last, Compare comp); 

的补偿是代表一个非常通用的方式“小于”操作算符/运营商,所以你不必有几分许多变种功能:

对于访问者模式,您希望实现类似的功能,但在运行时(迟)绑定的情况下:

您想简化A的接口,希望保留将来扩展的可能性(新操作使用A ),并且您希望在这些扩展的情况下实现A的接口的稳定性。

从原来的“胖”类:

class A 
{ 
    public: 
    virtual void function_or_operation_1();//this can be implemented in terms of public interface of the other functions 
    virtual void function_or_operation_2(); 
    //..etc 

    virtual void function_or_operation_N(); 
    public: 
    //stable public interface, some functions of procedures 

    private: 
    //.... 
} 

你从公共接口中删除尽可能多的功能(只要它们可以实现在同一个公共接口的非抽出功能方面利用由具有非常通用的接口

您减少在基类的函数数量的前进宣布Functor_or_Operator:) 和代表的操作从一个新的算符层次仿函数对象或对象

class Functor_or_Operator; 
class A 
{ 
    public: 
    virtual void apply(Functor_or_Operator*);//some generic function operates on this objects from A hierarchy 
    //..etc 
    public: 
    //stable public interface, some functions 

    private: 
    //.... 
} 

//现在在A层次结构(A,B,C)中有N(= 3)个类,而Functor_or_Operator层次结构中的类表示为M操作或函数 您需要实现N * M Functor_or_Operator的操作适用于A层次结构中的每个类。 最重要的是,你可以在不改变类'A'的接口的情况下做到这一点。 当引入新的操作或函数处理A层次对象 或A层次结构中的新派生类时,类A的声明在新增加的情况下变得非常稳定。 A的稳定性(在A不变的情况下)对于避免代价高昂(有时不可能)重新编译软件来说是很重要的,这些软件在很多地方都包含A的头文件。

对于A层次结构中的每个新类,您扩展了基础Functor_or_Operator的定义,添加新的实现文件,但是您永远不需要触及基类A的头(通常是接口类或抽象类)。

class Functor_or_Operator 
    { 
    virtual void apply(A*)=0; 
    virtual void apply(B*)=0; 
    virtual void apply(C*)=0; 
    } 

    void A::apply(Functor_or_Operator* f) 
    { f->apply(this);} //you need this only if A is not abstract (it is instantiable) 

    class B:public A 
    { 
    public: 
    void apply(Functor_or_Operator* f) { f->apply(this);} //dynamic dispatch , you call polymhorphic Functor f on this object 
    //..the rest of B implementation. 
    } 

    class C:public A 
    { 
    public: 
    void apply(Functor_or_Operator* f) { f->apply(this);} //dynamic dispatch , you call polymorfic Functor f on this object 
    //..the rest of C implementation. 
    } 

    class Functor_or_Operator_1:public Functor_or_Operator 
    { 
    public: 
     //implementations of application of a function represented by Functor_or_Operator_1 on each A,B,C 
     void apply(A*) {}//(only if A is instantiable,not an abstract class) 
     void apply(B*) {} 
     void apply(C*) {} 
    } 

    class Functor_or_Operator_2:public Functor_or_Operator 
    { 
    public: 
     //implementations of application of a function represented by Functor_or_Operator_2 on each A,B,C 
     void apply(A*) {}//(only if A is instantiable,not an abstract class) 
     void apply(B*) {} 
     void apply(C*) {} 
    }