什么是访问者模式?
在访问者模式(Visitor Pattern)中,我们使用了一个访问者类,它改变了元素类的执行算法。通过这种方式,元素的执行算法可以随着访问者改变而改变。这种类型的设计模式属于行为型模式。根据模式,元素对象已接受访问者对象,这样访问者对象就可以处理元素对象上的操作。
引出:
当你设计一个软件系统时,可能会遇到这样的问题:你需要对一个复杂的数据结构进行多种不同的操作,而且这些操作可能会频繁地变化。举个例子,假设你正在开发一个图形编辑器,用户可以在画布上创建各种形状,比如圆形、矩形、三角形等等。
现在,你需要实现一些功能来处理这些形状,比如计算每个形状的面积、绘制它们、或者进行其他一些操作。问题是,随着需求的变化,你可能会不断地添加新的操作,比如旋转、缩放、填充颜色等等。同时,形状的种类也可能会增加,比如新增了椭圆、星形等等。
在这样的情况下,如何设计软件系统使得在不断变化的操作和形状种类下,系统仍然能够保持灵活性和可扩展性呢?这就是引出访问者模式的现实问题。
通过访问者模式,你可以将操作与对象结构分离开来,每个具体的操作都由一个访问者类实现,这样你就可以轻松地添加新的操作而不必修改对象结构。同时,每个形状类只需要实现一个接受访问者的方法,而不需要知道具体的操作,这样就保持了系统的灵活性和可维护性。
介绍
意图:主要将数据结构与数据操作分离。
主要解决:稳定的数据结构和易变的操作耦合问题。
何时使用:需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而需要避免让这些操作"污染"这些对象的类,使用访问者模式将这些封装到类中。
如何解决:在被访问的类里面加一个对外提供接待访问者的接口。
关键代码:在数据基础类里面有一个方法接受访问者,将自身引用传入访问者。
应用实例:您在朋友家做客,您是访问者,朋友接受您的访问,您通过朋友的描述,然后对朋友的描述做出一个判断,这就是访问者模式。
使用场景: 1、对象结构中对象对应的类很少改变,但经常需要在此对象结构上定义新的操作。 2、需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而需要避免让这些操作"污染"这些对象的类,也不希望在增加新操作时修改这些类。
主要角色
- Visitor(访问者):为对象结构中的每个具体元素定义一个访问操作。
- ConcreteVisitor(具体访问者):实现每个具体元素的访问操作。
- Element(元素):定义一个接受访问者的方法,通常是一个抽象类或接口。
- ConcreteElement(具体元素):实现接受操作,并在其中调用访问者对应的访问操作。
- ObjectStructure(对象结构):能枚举它的元素,可以提供一个高层接口以允许访问者访问其元素。
C++实现:
下面通过一个具体的例子来展示访问者模式在C++中的实现。假设我们有一个对象结构,包含不同类型的形状(如圆形和矩形),我们希望对这些形状进行不同的操作(如计算面积和绘制)。
1. 定义访问者接口
class Circle;
class Rectangle;
class Visitor {
public:
virtual void visit(Circle *circle) = 0;
virtual void visit(Rectangle *rectangle) = 0;
virtual ~Visitor() = default;
};
2. 定义具体访问者
class AreaVisitor : public Visitor {
public:
void visit(Circle *circle) override;
void visit(Rectangle *rectangle) override;
};
class DrawVisitor : public Visitor {
public:
void visit(Circle *circle) override;
void visit(Rectangle *rectangle) override;
};
3. 定义元素接口
class Shape {
public:
virtual void accept(Visitor *visitor) = 0;
virtual ~Shape() = default;
};
4. 定义具体元素
class Circle : public Shape {
public:
void accept(Visitor *visitor) override {
visitor->visit(this);
}
double radius;
};
class Rectangle : public Shape {
public:
void accept(Visitor *visitor) override {
visitor->visit(this);
}
double width, height;
};
5. 实现具体访问者方法
#include <iostream>
void AreaVisitor::visit(Circle *circle) {
double area = 3.14 * circle->radius * circle->radius;
std::cout << "Circle Area: " << area << std::endl;
}
void AreaVisitor::visit(Rectangle *rectangle) {
double area = rectangle->width * rectangle->height;
std::cout << "Rectangle Area: " << area << std::endl;
}
void DrawVisitor::visit(Circle *circle) {
std::cout << "Drawing Circle with radius: " << circle->radius << std::endl;
}
void DrawVisitor::visit(Rectangle *rectangle) {
std::cout << "Drawing Rectangle with width: " << rectangle->width
<< " and height: " << rectangle->height << std::endl;
}
6. 使用访问者模式
int main() {
Circle circle;
circle.radius = 5;
Rectangle rectangle;
rectangle.width = 4;
rectangle.height = 6;
AreaVisitor areaVisitor;
DrawVisitor drawVisitor;
circle.accept(&areaVisitor);
circle.accept(&drawVisitor);
rectangle.accept(&areaVisitor);
rectangle.accept(&drawVisitor);
return 0;
}
访问者模式的优缺点
优点:
- 增加新的操作很容易:可以在不改变类的情况下增加新的操作。
- 集中相关操作:操作集中在访问者类中,而不是分散在元素类中。
缺点:
- 违反单一职责原则:元素类需要了解访问者接口,从而违反单一职责原则。
- 增加新的元素困难:如果需要增加新的元素类型,则需要修改所有的访问者。