目录
1、访问者模式含义
访问者模式(Visitor Pattern)是一种行为型设计模式,它允许将一个作用于某对象结构中的各元素的操作分离出来,封装成独立的类,使其在不改变数据结构的前提下可以添加作用于这些元素的新的操作,为数据结构中的每个元素提供多种访问方式。
该模式的核心思想是操作分离,通过定义一个外部访问者类来扩展复杂对象结构的功能,将数据结构与数据操作解耦,使得操作可以独立于对象的类来定义。
2、访问者模式的UML图学习
访问者模式的UML类图通常包含以下角色:
(1)抽象访问者(Visitor):声明了一个或者多个访问操作,形成所有的具体元素角色必须实现的接口。
(2)具体访问者(ConcreteVisitor):实现抽象访问者角色所声明的接口,也就是抽象访问者所声明的各个访问操作。
(3)抽象节点(Element):声明一个接受操作,接受一个访问者对象作为一个参量。
(4)具体节点(ConcreteNode):实现了抽象节点所规定的接受操作。
(5)结构对象(ObjectStructure):可以遍历结构中的所有元素;如果需要,提供一个高层次的接口让访问者对象可以访问每一个元素;如果需要,可以设计成一个复合对象或者一个聚集,如列(List)或集合(Set)。
3、访问者模式的应用场景
(1)数据结构稳定的场景:数据结构(如对象结构)相对稳定,但经常需要对其上的元素执行不同的操作时,访问者模式非常有用。
(2)需要多种不同操作而不希望修改元素类的场景:如果你有多个不同的操作需要应用到同一组元素上,且不希望在元素类中添加这些操作,访问者模式可以提供一种解决方案。
(3)需要跨不同类层次结构进行操作的场景:当需要在多个不相关的类层次结构上执行操作时,访问者模式可以将操作与类层次结构分离。
(4)需要在对象结构中执行复杂计算的场景:访问者模式可以用于在对象结构中执行复杂的计算或逻辑,而不需要修改元素类本身。
4、访问者模式的优缺点
(1)优点:
- 符合单一职责原则:访问者模式将元素操作封装在访问者类中,使得元素类专注于其核心职责,而不需要关心如何处理不同的操作。
- 优秀的扩展性:通过添加新的访问者类,可以很容易地扩展对象结构的功能,而不需要修改元素类。
- 聚合关系:访问者模式使得元素和访问者之间形成了一种聚合关系,从而提高了系统的灵活性和可维护性。
- 具体元素变更不频繁:如果元素类比较稳定,即不易发生变更,那么访问者模式将非常适用。
(2)缺点
- 增加具体元素和访问者类的难度:每次在元素结构中增加新的元素类时,都需要修改访问者类,这违反了开闭原则。
- 破坏封装:访问者模式需要访问元素类的内部数据和方法,这可能会破坏元素的封装性。
- 具体元素变更困难:如果元素类经常发生变更,那么访问者模式将变得难以维护,因为每次变更都需要修改访问者类。
5、访问者模式C++实现的实例
#include <iostream>
#include <vector>
// 元素接口
class Element {
public:
virtual void accept(class Visitor* visitor) = 0;
virtual ~Element() {}
};
// 具体元素A
class ConcreteElementA : public Element {
public:
void accept(Visitor* visitor) override {
visitor->visitConcreteElementA(this);
}
void operationA() {
std::cout << "ConcreteElementA's operationA" << std::endl;
}
};
// 具体元素B
class ConcreteElementB : public Element {
public:
void accept(Visitor* visitor) override {
visitor->visitConcreteElementB(this);
}
void operationB() {
std::cout << "ConcreteElementB's operationB" << std::endl;
}
};
// 访问者接口
class Visitor {
public:
virtual void visitConcreteElementA(ConcreteElementA* element) = 0;
virtual void visitConcreteElementB(ConcreteElementB* element) = 0;
virtual ~Visitor() {}
};
// 具体访问者
class ConcreteVisitor : public Visitor {
public:
void visitConcreteElementA(ConcreteElementA* element) override {
std::cout << "ConcreteVisitor visiting ConcreteElementA" << std::endl;
element->operationA();
}
void visitConcreteElementB(ConcreteElementB* element) override {
std::cout << "ConcreteVisitor visiting ConcreteElementB" << std::endl;
element->operationB();
}
};
// 对象结构
class ObjectStructure {
private:
std::vector<Element*> elements;
public:
void addElement(Element* element) {
elements.push_back(element);
}
void accept(Visitor* visitor) {
for (Element* element : elements) {
element->accept(visitor);
}
}
};
int main() {
ObjectStructure os;
os.addElement(new ConcreteElementA());
os.addElement(new ConcreteElementB());
ConcreteVisitor visitor;
os.accept(&visitor);
// 清理内存(在实际应用中,应使用智能指针等机制来管理内存)
// 这里为了简单起见,没有包含清理代码
return 0;
}
在这个实例中,Element
是元素接口,ConcreteElementA
和 ConcreteElementB
是具体元素类。Visitor
是访问者接口,ConcreteVisitor
是具体访问者类。ObjectStructure
是对象结构,它包含多个元素,并允许访问者访问这些元素。在 main
函数中,我们创建了一个对象结构,添加了一些元素,并使用访问者来访问这些元素。