- Published on
设计模式(23)——访问者 Visitor
- Authors
- Name
- Leon
二十三、Visitor(访问者模式,对象行为型模式)
1. 意图:
表示一个作用于某对象结构中的各元素的操作。它使你可以在不改变各元素的前提下定义作用于这些元素的新操作。
2. 适用:
- 一个对象结构中包含很多类对象,它们有不同的接口,而你想对这些对象实施一些依赖于其具体类的操作。
- 需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而你想避免让这些操作“污染”这些对象的类。Visitor 使得你可以将相关的操作集中起来定义在一个类中。当该对象被很多应用共享时,用 Visitor 模式让每个应用仅包含需要用到的操作。
- 定义对象结构的类很少改变,但经常需要在此结构上定义新的操作。改变对象结构类需要重定义对所有访问者的接口,这可能需要很大的代价。如果对象结构类经常改变,那么可能还是在这些类中定义这些操作较好。
3. 类图:
4. 相关模式:
Composite: 访问者可以用于对一个由 Composite 模式定义的对象结构进行操作。 Interpreter: 访问者可以用于解释。
5. 思考:
Visitor 模式可以使得Element在不修改自己的同时增加新的操作,但是这也带来了至少以下两个显著问题:
- 破坏了封装性。Visitor 模式要求 Visitor 可以从外部修改 Element 对象的状态,这一般通过两个方式来实现:
- Element 提供足够的 public 接口,使得 Visitor 可以通过调用这些接口达到修改 Element 状态的目的;
- Element 暴露更多的细节给 Visitor,或者让 Element 提供 public 的实现给 Visitor(当然也给了系统中其他的对象),或者将 Visitor 声明为 Element 的 friend 类,仅将细节暴露给 Visitor。但是无论哪种情况,特别是后者都将是破坏了封装性原则(实际上就是 C++ 的 friend 机制得到了很多的面向对象专家的诟病)。
- ConcreteElement 的扩展很困难:每增加一个 Element 子类,就要修改 Visitor 的接口,使得可以提供给这个新增加的子类的访问机制。
6. C++实现:
- 编写元素抽象基类
Element
,包含一个纯虚函数(接口方法)Accept(Visitor* vis)
- 编写访问者抽象基类
Visitor
,包含访问元素的方法VisitConcreteElementA(Element* elm)
、VisitConcreteElementB(Element* elm)
- 元素的子类定义
Accept
函数,调用具体访问者的具体访问元素方法VisitConcreteElementA(Element* elm)
、VisitConcreteElementB(Element* elm)
Visitor.h
//Visitor.h
#pragma once
class ConcreteElementA;
class ConcreteElementB;
class Element;
class Visitor {
public:
virtual ~Visitor();
virtual void VisitConcreteElementA(Element* elm) = 0;
virtual void VisitConcreteElementB(Element* elm) = 0;
protected:
Visitor();
private:
};
class ConcreteVisitorA : public Visitor {
public:
ConcreteVisitorA();
virtual ~ConcreteVisitorA();
virtual void VisitConcreteElementA(Element* elm);
virtual void VisitConcreteElementB(Element* elm);
protected:
private:
};
class ConcreteVisitorB : public Visitor {
public:
ConcreteVisitorB();
virtual ~ConcreteVisitorB();
virtual void VisitConcreteElementA(Element* elm);
virtual void VisitConcreteElementB(Element* elm);
protected:
private:
};
Visitor.cpp
//Visitor.cpp
#include "Visitor.h"
#include "Element.h"
#include <iostream>
using namespace::std;
Visitor::Visitor() {}
Visitor::~Visitor() {}
ConcreteVisitorA::ConcreteVisitorA() {}
ConcreteVisitorA::~ConcreteVisitorA() {}
void ConcreteVisitorA::VisitConcreteElementA(Element* elm) {
cout << "i will visit ConcreteElementA..." << endl;
}
void ConcreteVisitorA::VisitConcreteElementB(Element* elm) {
cout << "i will visit ConcreteElementB..." << endl;
}
ConcreteVisitorB::ConcreteVisitorB() {}
ConcreteVisitorB::~ConcreteVisitorB() {}
void ConcreteVisitorB::VisitConcreteElementA(Element* elm) {
cout << "i will visit ConcreteElementA..." << endl;
}
void ConcreteVisitorB::VisitConcreteElementB(Element* elm) {
cout << "i will visit ConcreteElementB..." << endl;
}
Element.h
//Element.h
#pragma once
class Visitor;
class Element {
public:
virtual ~Element();
virtual void Accept(Visitor* vis) = 0;
protected:
Element();
private:
};
class ConcreteElementA : public Element {
public:
ConcreteElementA();
~ConcreteElementA();
void Accept(Visitor* vis);
protected:
private:
};
class ConcreteElementB : public Element {
public:
ConcreteElementB();
~ConcreteElementB();
void Accept(Visitor* vis);
protected:
private:
};
Element.cpp
//Element.cpp
#include "Element.h"
#include "Visitor.h"
#include <iostream>
using namespace::std;
Element::Element() {}
Element::~Element() {}
void Element::Accept(Visitor* vis){}
ConcreteElementA::ConcreteElementA() {}
ConcreteElementA::~ConcreteElementA() {}
void ConcreteElementA::Accept(Visitor* vis) {
vis->VisitConcreteElementA(this);
cout << "visiting ConcreteElementA..." << endl;
}
ConcreteElementB::ConcreteElementB(){}
ConcreteElementB::~ConcreteElementB() {}
void ConcreteElementB::Accept(Visitor* vis) {
vis->VisitConcreteElementB(this);
cout << "visiting ConcreteElementB..." << endl;
}
main.cpp
//main.cpp
#include "Visitor.h"
#include "Element.h"
#include <iostream>
using namespace::std;
int main(int argc, char* argv[]) {
Visitor* vis = new ConcreteVisitorA();
Element* elm = new ConcreteElementA();
elm->Accept(vis);
return 0;
}