C++访问者模式
意图
表示一个作用于某对象结构中的各元素的操作。它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作。
适用性
- 一个对象结构包含很多类对象,它们有不同的接口,而你想对这些对象实施一些依赖于其具体类的操作。
- 需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而你想避免让这些操作 “污染” 这些对象的类。 Visitor 使得你可以将相关的操作集中起来定义在一个类中。当该对象结构被很多应用共享时,用 Vistor 模式让每个应用仅包含需要用到的操作。
- 定义对象结构的类很少改变,但经常需要在此结构上定义新的操作。改变对象结构类需要重定义对所有访问者的接口,这可能需要很大的代价。如果对象结构类京城改变,那么可能还是在这些类中定义这些操作较好。
模型结构
- Visitor:接口或者抽象类,定义了对每个 Element 访问的行为,它的参数就是被访问的元素,它的方法个数理论上与元素的个数是一样的,因此,访问者模式要求元素的类型要稳定,如果经常添加、移除元素类,必然会导致频繁地修改 Visitor 接口,如果出现这种情况,则说明不适合使用访问者模式。
- ConcreteVisitor:具体的访问者,它需要给出对每一个元素类访问时所产生的具体行为。
- Element:元素接口或者抽象类,它定义了一个接受访问者(accept)的方法,其意义是指每一个元素都要可以被访问者访问。
- ElementA、ElementB:具体的元素类,它提供接受访问的具体实现,而这个具体的实现,通常情况下是使用访问者提供的访问该元素类的方法。
- ObjectStructure:定义当中所提到的对象结构,对象结构是一个抽象表述,它内部管理了元素集合,并且可以迭代这些元素提供访问者访问。
示意性代码
#include <iostream>
#include <string>
using namespace std;
class CPU; // 前向声明
class HardDisk; // 前向声明
class Visitor {
public:
virtual ~Visitor() { }
virtual void visitCPU(CPU* cpu) = 0;
virtual void visitDisk(HardDisk* disk) = 0;
};
class Hardware {
public:
Hardware(const string& command) {
this->m_command = command;
}
virtual ~Hardware() { }
void run() {
cout << m_command << endl;
}
virtual void accept(Visitor* visitor) = 0;
public:
string m_command;
};
class CPU : public Hardware {
public:
CPU(const string& command) : Hardware(command) { }
void accept(Visitor* visitor) override {
visitor->visitCPU(this);
}
};
class HardDisk : public Hardware {
public:
HardDisk(const string& command) : Hardware(command) { }
void accept(Visitor* visitor) override {
visitor->visitDisk(this);
}
};
class UpdateVisitor : public Visitor {
public:
void visitCPU(CPU* cpu) override {
cpu->m_command = "1 + 1 = 2";
}
void visitDisk(HardDisk* disk) override {
disk->m_command = "记住: 1 + 1 = 2";
}
};
class EggRobbt {
public:
EggRobbt() {
m_disk = new HardDisk("记住: 1 + 1 = 1");
m_cpu = new CPU("1 + 1 = 1");
}
void calc() {
m_cpu->run();
m_disk->run();
}
void accept(Visitor* visitor) {
m_cpu->accept(visitor);
m_disk->accept(visitor);
}
private:
HardDisk* m_disk;
CPU* m_cpu;
};
int main() {
EggRobbt erDan;
erDan.calc();
Visitor* updatePack = new UpdateVisitor();
erDan.accept(updatePack);
erDan.calc();
}