Published on

设计模式(23)——访问者 Visitor

Authors
  • avatar
    Name
    Leon
    Twitter

二十三、Visitor(访问者模式,对象行为型模式)

1. 意图:

  表示一个作用于某对象结构中的各元素的操作。它使你可以在不改变各元素的前提下定义作用于这些元素的新操作。

2. 适用:

  1. 一个对象结构中包含很多类对象,它们有不同的接口,而你想对这些对象实施一些依赖于其具体类的操作。
  2. 需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而你想避免让这些操作“污染”这些对象的类。Visitor 使得你可以将相关的操作集中起来定义在一个类中。当该对象被很多应用共享时,用 Visitor 模式让每个应用仅包含需要用到的操作。
  3. 定义对象结构的类很少改变,但经常需要在此结构上定义新的操作。改变对象结构类需要重定义对所有访问者的接口,这可能需要很大的代价。如果对象结构类经常改变,那么可能还是在这些类中定义这些操作较好。

3. 类图:

4. 相关模式:

  Composite: 访问者可以用于对一个由 Composite 模式定义的对象结构进行操作。   Interpreter: 访问者可以用于解释。

5. 思考:

  Visitor 模式可以使得Element在不修改自己的同时增加新的操作,但是这也带来了至少以下两个显著问题:

  1. 破坏了封装性。Visitor 模式要求 Visitor 可以从外部修改 Element 对象的状态,这一般通过两个方式来实现:
  2. Element 提供足够的 public 接口,使得 Visitor 可以通过调用这些接口达到修改 Element 状态的目的;
  3. Element 暴露更多的细节给 Visitor,或者让 Element 提供 public 的实现给 Visitor(当然也给了系统中其他的对象),或者将 Visitor 声明为 Element 的 friend 类,仅将细节暴露给 Visitor。但是无论哪种情况,特别是后者都将是破坏了封装性原则(实际上就是 C++ 的 friend 机制得到了很多的面向对象专家的诟病)。
  4. ConcreteElement 的扩展很困难:每增加一个 Element 子类,就要修改 Visitor 的接口,使得可以提供给这个新增加的子类的访问机制。

6. C++实现:

  1. 编写元素抽象基类 Element,包含一个纯虚函数(接口方法)Accept(Visitor* vis)
  2. 编写访问者抽象基类 Visitor,包含访问元素的方法 VisitConcreteElementA(Element* elm)VisitConcreteElementB(Element* elm)
  3. 元素的子类定义 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;
}