参考:
模板方法设计模式 (refactoringguru.cn)
design-patterns-cpp/TemplateMethod.cpp at master · JakubVojvoda/design-patterns-cpp · GitHubhttps://github.com/JakubVojvoda/design-patterns-cpp/blob/master/state/State.cpp)
文章目录
- 一、什么是访问者模式?
- 二、实现
- 三、优缺点
- 优点
- 缺点
- 四、适用环境
一、什么是访问者模式?
在软件构建过程中,由于需求的改变,某些类层次结构中常常需要增加新的行为(方法),如果直接在基类中做这样的更改,将会给子类带来很繁重的变更负担,甚至破坏原有设计。
访问者模式定义:表示一个作用于某对象结构中的各元素的操作。使得可以在不改变(稳定)各元素的类的前提下定义(扩展)作用于这些元素的新操作(变化)。
假如你需要将数据对象存储到xml文件中,而这样的数据类型有很多种。你不可能为每个类添加一个存储为xml的方法,因为后续可能又需要将数据存储其它格式,比如json,csv等。而存储的实现,又需要访问所有数据类的具体成员,这个时候,访问者模式可以很好地解决这个问题。
二、实现
访问者(Visitor)模式主要包含以下角色:
1、访问者(Visitor):接口,声明了一系列以对象结构的具体元素为参数的访问者方法。如果编程语言支持重载,这些方法的名称可以是相同的,但是其参数一定是不同的。
2、具体访问者(Concrete Visitor):会为不同的具体元素类实现相同行为的几个不同版本。
3、元素(Element):接口,声明了一个方法来接收访问者。该方法必须有一个参数被声明为访问者接口类型。
4、具体元素(Concrete Element):必须实现接收方法。该方法的目的是根据当前元素类将其调用重定向到相应访问者的方法。请注意,即使元素基类实现了该方法,所有子类都必须对其进行重写并调用访问者对象中的合适方法。
/*
* C++ Design Patterns: Visitor
* Author: Jakub Vojvoda [github.com/JakubVojvoda]
* 2016
*
* Source code is licensed under MIT License
* (for more details see LICENSE)
*
*/
#include <iostream>
class Element;
class ConcreteElementA;
class ConcreteElementB;
/*
* Visitor
* declares a Visit operation for each class of ConcreteElement
* in the object structure
*/
class Visitor
{
public:
virtual ~Visitor() {}
virtual void visitElementA( ConcreteElementA* const element ) = 0;
virtual void visitElementB( ConcreteElementB* const element ) = 0;
// ...
};
/*
* Concrete Visitors
* implement each operation declared by Visitor, which implement
* a fragment of the algorithm defined for the corresponding class
* of object in the structure
*/
class ConcreteVisitor1 : public Visitor
{
public:
~ConcreteVisitor1() {}
void visitElementA( ConcreteElementA* const )
{
std::cout << "Concrete Visitor 1: Element A visited." << std::endl;
}
void visitElementB( ConcreteElementB* const )
{
std::cout << "Concrete Visitor 1: Element B visited." << std::endl;
}
// ...
};
class ConcreteVisitor2 : public Visitor
{
public:
~ConcreteVisitor2() {}
void visitElementA( ConcreteElementA* const )
{
std::cout << "Concrete Visitor 2: Element A visited." << std::endl;
}
void visitElementB( ConcreteElementB* const )
{
std::cout << "Concrete Visitor 2: Element B visited." << std::endl;
}
// ...
};
/*
* Element
* defines an accept operation that takes a visitor as an argument
*/
class Element
{
public:
virtual ~Element() {}
virtual void accept( Visitor &visitor ) = 0;
// ...
};
/*
* Concrete Elements
* implement an accept operation that takes a visitor as an argument
*/
class ConcreteElementA : public Element
{
public:
~ConcreteElementA() {}
void accept( Visitor &visitor )
{
visitor.visitElementA( this );
}
// ...
};
class ConcreteElementB : public Element
{
public:
~ConcreteElementB() {}
void accept( Visitor &visitor )
{
visitor.visitElementB( this );
}
// ...
};
int main()
{
ConcreteElementA elementA;
ConcreteElementB elementB;
ConcreteVisitor1 visitor1;
ConcreteVisitor2 visitor2;
elementA.accept(visitor1);
elementA.accept(visitor2);
elementB.accept(visitor1);
elementB.accept(visitor2);
return 0;
}
三、优缺点
优点
- 开闭原则。你可以引入在不同类对象上执行的新行为,且无需对这些类做出修改。
- 单一职责原则。可将同一行为的不同版本移到同一个类中。
- 访问者对象可以在与各种对象交互时收集一些有用的信息。当你想要遍历一些复杂的对象结构(例如对象树),并在结构中的每个对象上应用访问者时,这些信息可能会有所帮助。
缺点
- 部分客户端可能会受到算法框架的限制。
- 通过子类抑制默认步骤实现可能会导致违反里氏替换原则。
- 只要算法发生变化,你就可能需要修改所有的类。
四、适用环境
- 如果你需要对一个复杂对象结构(例如对象树)中的所有元素执行某些操作,可使用访问者模式。
- 当某个行为仅在类层次结构中的一些类中有意义,而在其他类中没有意义时,可使用该模式。