访问者模式(Visitor Pattern)
🧠 访问者模式(Visitor Pattern)
访问者模式是一种行为型设计模式,它允许你在不改变对象结构的情况下,增加新的操作。简单来说,访问者模式可以使得你在不修改类的情况下,向已有的类中添加新的操作逻辑。
访问者模式的核心思想是:将数据结构与操作分离,把操作封装到访问者中。可以使得新增的操作可以通过访问者去执行,而无需修改数据结构本身。
🎯 为什么需要访问者模式?
- 扩展性强:访问者模式可以很容易地扩展新的操作,而不需要修改现有的数据结构或类。
- 双重分离:将操作和数据结构分离,允许数据结构和操作分开变化,符合“开闭原则”,易于维护和拓展。
- 封装行为:通过访问者模式封装多种操作,并避免修改原始类,减少类的复杂度。
✅ 优缺点分析
✅ 优点 | ❌ 缺点 |
---|---|
增加新的操作时不需要修改原有对象结构,符合开闭原则 | 增加新的数据结构类时需要修改所有访问者,不利于数据结构的扩展 |
使得代码更符合“单一职责原则”,因为每个访问者只关心一种操作 | 如果数据结构变化频繁,修改访问者可能成为负担 |
易于扩展新的操作而不影响原有代码 | 复杂的系统中,访问者可能会增多,导致难以维护 |
🧩 Python 示例:图形绘制应用
假设有一个图形库,包含多个不同的形状:圆形、矩形,我们希望能够对这些图形进行多个操作,例如计算面积、计算周长等。使用访问者模式可以实现动态增加新的操作,而不需要修改已有的图形类。
🎯 1️⃣ 图形接口(Shape)
from abc import ABC, abstractmethodclass Shape(ABC):@abstractmethoddef accept(self, visitor):pass
Shape
是所有图形的抽象类,定义了accept()
方法,接受一个访问者对象来执行特定的操作。
🎯 2️⃣ 具体图形类(Concrete Shapes)
class Circle(Shape):def __init__(self, radius):self.radius = radiusdef accept(self, visitor):visitor.visit_circle(self)class Rectangle(Shape):def __init__(self, width, height):self.width = widthself.height = heightdef accept(self, visitor):visitor.visit_rectangle(self)
Circle
和Rectangle
类是具体的图形类,分别代表圆形和矩形。- 每个图形类都实现了
accept()
方法,接受一个访问者,并将自身传递给访问者进行操作。
🎯 3️⃣ 访问者接口(Visitor)
class Visitor(ABC):@abstractmethoddef visit_circle(self, circle: Circle):pass@abstractmethoddef visit_rectangle(self, rectangle: Rectangle):pass
Visitor
类是一个抽象类,定义了每个图形类的访问方法。例如,visit_circle()
用于访问Circle
对象,visit_rectangle()
用于访问Rectangle
对象。
🎯 4️⃣ 具体访问者(Concrete Visitor)
class AreaVisitor(Visitor):def visit_circle(self, circle: Circle):# 计算圆形面积print(f"Area of Circle: {3.14 * circle.radius ** 2}")def visit_rectangle(self, rectangle: Rectangle):# 计算矩形面积print(f"Area of Rectangle: {rectangle.width * rectangle.height}")class PerimeterVisitor(Visitor):def visit_circle(self, circle: Circle):# 计算圆形周长print(f"Perimeter of Circle: {2 * 3.14 * circle.radius}")def visit_rectangle(self, rectangle: Rectangle):# 计算矩形周长print(f"Perimeter of Rectangle: {2 * (rectangle.width + rectangle.height)}")
AreaVisitor
和PerimeterVisitor
是具体的访问者类,分别用于计算图形的面积和周长。- 它们实现了
Visitor
接口的visit_circle()
和visit_rectangle()
方法。
🎯 5️⃣ 客户端代码(执行操作)
# 创建图形
circle = Circle(5)
rectangle = Rectangle(4, 6)# 创建访问者
area_visitor = AreaVisitor()
perimeter_visitor = PerimeterVisitor()# 执行操作
circle.accept(area_visitor) # 计算圆形的面积
circle.accept(perimeter_visitor) # 计算圆形的周长rectangle.accept(area_visitor) # 计算矩形的面积
rectangle.accept(perimeter_visitor) # 计算矩形的周长
- 我们创建了
Circle
和Rectangle
两个图形对象。 - 我们还创建了两个访问者:
AreaVisitor
和PerimeterVisitor
,用于计算图形的面积和周长。 - 通过
accept()
方法,图形对象将自己传递给访问者,访问者执行相应的操作。
✅ 输出结果
Area of Circle: 78.5
Perimeter of Circle: 31.400000000000002
Area of Rectangle: 24
Perimeter of Rectangle: 20
AreaVisitor
计算了圆形和矩形的面积。PerimeterVisitor
计算了圆形和矩形的周长。
🧭 类图(Mermaid)
🧭 流程图(Mermaid)
🧠 应用场景总结
场景 | 示例 |
---|---|
计算、处理复杂对象 | 例如计算复合图形的面积和周长 |
扩展现有类的功能 | 在不改变原始类的情况下添加新的操作 |
对象结构不常变化,但操作可能变化 | 比如一个系统中对象结构不变,但可能需要动态改变操作行为 |
✅ 总结口诀
访问者模式:
✅ 将操作封装到独立的访问者类中,避免修改原有类结构,符合开闭原则。
✅ 适用于需要在一个不常变化的对象结构上,动态增加新操作的场景。
如果你对访问者模式有其他问题或想了解更多细节,欢迎继续提问!