22.访问者模式
设计模式——访问者模式
一、基本概念
1. 定义
访问者(Visitor)模式:将作用于某种数据结构中的各元素的操作分离出来封装成独立的类,使其在不改变数据结构的前提下可以添加作用于这些元素的新的操作,为数据结构中的每个元素提供多种访问方式。它将对数据的操作与数据结构进行分离,是行为类模式中最复杂的一种模式。
访问者模式是一种行为设计模式, 它能将算法与其所作用的对象隔离开来。
2. 优缺点
优点:
- 扩展性好。能够在不修改对象结构中的元素的情况下,为对象结构中的元素添加新的功能;
- 复用性好。可以通过访问者来定义整个对象结构通用的功能,从而提高系统的复用程度;
- 灵活性好。访问者模式将数据结构与作用于结构上的操作解耦,使得操作集合可相对自由地演化而不影响系统的数据结构;
- 符合单一职责原则。访问者模式把相关的行为封装在一起,构成一个访问者,使每一个访问者的功能都比较单一。
缺点:
- 增加新的元素类很困难。在访问者模式中,每增加一个新的元素类,都要在每一个具体访问者类中增加相应的具体操作,这违背了“开闭原则”;
- 破坏封装。访问者模式中具体元素对访问者公布细节,这破坏了对象的封装性;
- 违反了依赖倒置原则。访问者模式依赖了具体类,而没有依赖抽象类。
3. 结构
- 抽象访问者(Visitor)角色:定义一个访问具体元素的接口,为每个具体元素类对应一个访问操作 visit() ,该操作中的参数类型标识了被访问的具体元素;
- 具体访问者(ConcreteVisitor)角色:实现抽象访问者角色中声明的各个访问操作,确定访问者访问一个元素时该做什么;
- 抽象元素(Element)角色:声明一个包含接受操作 accept() 的接口,被接受的访问者对象作为 accept() 方法的参数;
- 具体元素(ConcreteElement)角色:实现抽象元素角色提供的 accept() 操作,其方法体通常都是 visitor.visit(*this) ,另外具体元素中可能还包含本身业务逻辑的相关操作;
- 对象结构(Object Structure)角色:是一个包含元素角色的容器,提供让访问者对象遍历容器中的所有元素的方法,通常由 std::vector、std::set、std::map 等容器实现。
classDiagram
class Visitor {
<<abstract>>
+visitConcreteElementA(ConcreteElementA) void
+visitConcreteElementB(ConcreteElementB) void
}
class ConcreteVisitorA {
+visitConcreteElementA(ConcreteElementA) void
+visitConcreteElementB(ConcreteElementB) void
}
class ConcreteVisitorB {
+visitConcreteElementA(ConcreteElementA) void
+visitConcreteElementB(ConcreteElementB) void
}
class Element {
<<abstract>>
+accept(Visitor) void
}
class ConcreteElementA {
+accept(Visitor) void
}
class ConcreteElementB {
+accept(Visitor) void
}
class ObjectStructure {
-list~Element~ elements
+attach(Element) void
+detach(Element) void
+accept(Visitor) void
}
Visitor <|-- ConcreteVisitorA
Visitor <|-- ConcreteVisitorB
Element <|-- ConcreteElementA
Element <|-- ConcreteElementB
ObjectStructure o--> Element : 遍历
ConcreteElementA ..> Visitor : accept 双分派
ConcreteElementB ..> Visitor : accept 双分派
二、代码实现
抽象访问者
定义一个访问具体元素的接口,为每个具体元素类对应一个访问操作 visit() ,该操作中的参数类型标识了被访问的具体元素:
class ConcreteElementA;class ConcreteElementB;
class Visitor {public: virtual ~Visitor() = default; virtual void visit(ConcreteElementA& element) = 0; virtual void visit(ConcreteElementB& element) = 0;};抽象元素角色
声明一个包含接受操作 accept() 的接口,被接受的访问者对象作为 accept() 方法的参数:
class Element {public: virtual ~Element() = default; virtual void accept(Visitor& visitor) = 0;};具体元素角色
实现抽象元素角色提供的 accept() 操作,其方法体通常都是 visitor.visit(*this) ,另外具体元素中可能还包含本身业务逻辑的相关操作:
#include <string>
class ConcreteElementA : public Element {public: void accept(Visitor& visitor) override { visitor.visit(*this); }
std::string operationA() { return "具体元素A的操作!"; }};#include <string>
class ConcreteElementB : public Element {public: void accept(Visitor& visitor) override { visitor.visit(*this); }
std::string operationB() { return "具体元素B的操作!"; }};具体访问者
实现抽象访问者角色中声明的各个访问操作,确定访问者访问一个元素时该做什么:
#include <iostream>#include <string>
class ConcreteVisitorA : public Visitor {public: void visit(ConcreteElementA& element) override { std::cout << "具体访问者A访问: " << element.operationA() << std::endl; }
void visit(ConcreteElementB& element) override { std::cout << "具体访问者A访问: " << element.operationB() << std::endl; }};#include <iostream>#include <string>
class ConcreteVisitorB : public Visitor {public: void visit(ConcreteElementA& element) override { std::cout << "具体访问者B访问: " << element.operationA() << std::endl; }
void visit(ConcreteElementB& element) override { std::cout << "具体访问者B访问: " << element.operationB() << std::endl; }};对象结构角色
是一个包含元素角色的容器,提供让访问者对象遍历容器中的所有元素的方法:
#include <algorithm>#include <memory>#include <vector>
class ObjectStructure {public: void accept(Visitor& visitor) { for (const auto& element : list) { element->accept(visitor); } }
void add(std::unique_ptr<Element> element) { list.push_back(std::move(element)); }
void remove(Element* element) { auto it = std::find_if(list.begin(), list.end(), [element](const std::unique_ptr<Element>& e) { return e.get() == element; }); if (it != list.end()) { list.erase(it); } }
private: std::vector<std::unique_ptr<Element>> list;};客户类
#include <iostream>#include <memory>
int main() { ObjectStructure objectStructure; objectStructure.add(std::make_unique<ConcreteElementA>()); objectStructure.add(std::make_unique<ConcreteElementB>());
ConcreteVisitorA visitorA; ConcreteVisitorB visitorB; Visitor* visitor = &visitorA; objectStructure.accept(*visitor); std::cout << "_______________" << std::endl; visitor = &visitorB; objectStructure.accept(*visitor); return 0;}运行结果:
具体访问者A访问: 具体元素A的操作!具体访问者A访问: 具体元素B的操作!_______________具体访问者B访问: 具体元素A的操作!具体访问者B访问: 具体元素B的操作!参考:
Thanks for reading!