概念
表示一个作用于某对象结构中的各元素的操作.它使你可以在不改变各元素的类 的前提下定义作用于这些元素的新操作.
动机
在软件构建过程中,由于需求的改变,某些类层次结构中常常需要增加新的行为(方法),如果直接在基类中做这样的更改,将会给子类带来很繁重的变更负担,甚至破坏原有设计。
如何在不更改类层次结构的前提下,在运行时根据需要透明地为类层次结构上的各个类动态添加新的操作,从而避免上述问题?
抽象访问者(Visitor)角色:声明了一个或者多个访问操作,形成所有的具体元素角色必须实现的接口。
具体访问者(ConcreteVisitor)角色:实现抽象访问者角色所声明的接口,也就是抽象访问者所声明的各个访问操作。
抽象节点(Element)角色:声明一个接受操作,接受一个访问者对象作为一个参量。
具体节点(ConcreteElement)角色:实现了抽象元素所规定的接受操作。
结构对象(ObiectStructure)角色:有如下的一些责任,可以遍历结构中的所有元素;如果需要,提供一个高层次的接口让访问者对象可以访问每一个元素;如果需要,可以设计成一个复合对象或者一个聚集,如列(List)或集合(Set)。
适用于:
把数据结构 和 作用于数据结构上的操作 进行解耦合;
适用于数据结构比较稳定的场合
访问者模式总结:
访问者模式优点是增加新的操作很容易,因为增加新的操作就意味着增加一个新的访问者。访问者模式将有关的行为集中到一个访问者对象中。访问者模式的缺点是增加新的数据结构变得困难了
Visitor 模式把对结点的访问封装成一个抽象基类,通过派生出不同的类生成新 的访问方式.在实现的时候,在 visitor 抽象基类中声明了对所有不同结点进行访 问的接口函数,如图中的 VisitConcreateElementA 函数等,这样也造成了 Visit or模式的一个缺陷–新加入一个结点的时候都要添加Visitor中的对其进行访问 接口函数,这样使得所有的Visitor及其派生类都要重新编译了,也就是说Visitor 模式一个缺点就是添加新的结点十分困难.另外,还需要指出的是 Visitor 模式采 用了所谓的"双重分派"的技术,拿上图来作为例子,要对某一个结点进行访问,首 先需要产生一个 Element 的派生类对象,其次要传入一个 Visitor 类派生类对象 来调用对应的Accept 函数,也就是说,到底对哪种Element 采用哪种 Visitor访 问,需要两次动态绑定才可以确定下来
#include <iostream>
using namespace std;
class Visitor;
class Element
{
public:
virtual void accept(Visitor& visitor) = 0; //第一次多态辨析
virtual ~Element(){}
};
class ElementA : public Element
{
public:
void accept(Visitor &visitor) override {
visitor.visitElementA(*this);
}
};
class ElementB : public Element
{
public:
void accept(Visitor &visitor) override {
visitor.visitElementB(*this); //第二次多态辨析
}
};
class Visitor{
public:
virtual void visitElementA(ElementA& element) = 0;
virtual void visitElementB(ElementB& element) = 0;
virtual ~Visitor(){}
};
//==================================
//扩展1
class Visitor1 : public Visitor{
public:
void visitElementA(ElementA& element) override{
cout << "Visitor1 is processing ElementA" << endl;
}
void visitElementB(ElementB& element) override{
cout << "Visitor1 is processing ElementB" << endl;
}
};
//扩展2
class Visitor2 : public Visitor{
public:
void visitElementA(ElementA& element) override{
cout << "Visitor2 is processing ElementA" << endl;
}
void visitElementB(ElementB& element) override{
cout << "Visitor2 is processing ElementB" << endl;
}
};
int main()
{
Visitor2 visitor;
ElementB elementB;
elementB.accept(visitor);// double dispatch
ElementA elementA;
elementA.accept(visitor);
return 0;
}
总结
Vitisor模式通过所谓双重分发来实现在不更改Element类层次结构得前提下,在运行时透明地为类层次结构上地各种类动态添加新的操作(支持变化)。
所谓双重分发即Visitor模式中间包括了两个多态分发:第一个为accept方法地多态辨析;第二个时visitElementX方法地多态辨析。
Visitor模式地最大缺点在于扩展类层次结果,会导致Visitor类地改变。因此Visit模式使用于“Element类层次结构稳定,而其中地操作却经常面临频繁改动”。