行为型 困难

访问者模式

Visitor Pattern

表示一个作用于某对象结构中的各元素的操作。它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作。

概述

访问者模式是一种行为设计模式,它能将算法与其所作用的对象隔离开来。访问者模式允许你在不修改已有类的情况下向已有类层次结构中增加新的行为。它通过双重分派机制实现:第一次分派是元素调用访问者的visit方法,第二次分派是访问者根据元素类型调用对应的visit方法。

适用场景

  • 当需要对一个复杂对象结构(例如对象树)中的所有元素执行某些操作时
  • 当需要清理复杂对象结构的逻辑和业务逻辑时
  • 当某个行为仅在类层次结构中的一些类中有意义,而在其他类中没有意义时
  • 当需要频繁地添加新的操作,而不想修改元素类时
  • 当元素类层次结构稳定,但操作经常变化时

优点

  • +开闭原则:可以引入在不同类对象上执行的新行为,且无需修改这些类
  • +单一职责原则:可将同一行为的不同版本移到同一个类中
  • +访问者对象可以在与各种对象交互时收集一些有用的信息
  • +更容易添加新的操作,只需添加新的访问者类即可

缺点

  • 每次在元素层次结构中添加或移除一个类时,都要更新所有的访问者
  • 访问者可能缺乏访问元素私有成员变量和方法的必要权限
  • 增加了代码的复杂性,需要理解双重分派机制
  • 违反了依赖倒置原则,访问者依赖于具体的元素类

结构

classDiagram class Visitor { <<interface>> +visitConcreteElementA(ConcreteElementA) +visitConcreteElementB(ConcreteElementB) } class ConcreteVisitor1 { +visitConcreteElementA(ConcreteElementA) +visitConcreteElementB(ConcreteElementB) } class ConcreteVisitor2 { +visitConcreteElementA(ConcreteElementA) +visitConcreteElementB(ConcreteElementB) } class Element { <<interface>> +accept(Visitor) } class ConcreteElementA { +accept(Visitor) +operationA() } class ConcreteElementB { +accept(Visitor) +operationB() } class ObjectStructure { -elements: List~Element~ +attach(Element) +detach(Element) +accept(Visitor) } class Client { +main() } Visitor <|-- ConcreteVisitor1 Visitor <|-- ConcreteVisitor2 Element <|-- ConcreteElementA Element <|-- ConcreteElementB ObjectStructure o-- Element Client ..> Visitor : uses Client ..> ObjectStructure : uses ConcreteElementA ..> Visitor : accepts ConcreteElementB ..> Visitor : accepts style Visitor fill:#e3f2fd,stroke:#1976d2,stroke-width:2px style Element fill:#e3f2fd,stroke:#1976d2,stroke-width:2px style ConcreteVisitor1 fill:#f3e5f5,stroke:#7b1fa2,stroke-width:1px style ConcreteVisitor2 fill:#f3e5f5,stroke:#7b1fa2,stroke-width:1px style ConcreteElementA fill:#fff3e0,stroke:#f57c00,stroke-width:1px style ConcreteElementB fill:#fff3e0,stroke:#f57c00,stroke-width:1px style ObjectStructure fill:#e8f5e9,stroke:#388e3c,stroke-width:1px style Client fill:#f5f5f5,stroke:#616161,stroke-width:1px

交互演示

分离算法与对象结构
元素结构
City
Industry
Sight
访问者
访问结果等待执行...

选择访问者并执行,观察如何在不改变元素类的情况下添加新操作

生活类比

访问者模式就像是税务检查员访问不同企业

税务检查

税务检查员(访问者)访问不同的企业(元素),对每个企业执行相同的检查操作,但处理细节因企业类型(工厂、商店、餐厅)而异。检查员无需改变企业本身的运营方式,就能完成检查工作。

医生查房

医生(访问者)查房时会访问不同的病人(元素),对每个病人进行检查、诊断,但不同病人(内科、外科、儿科)需要不同的检查方式和处理。

保险评估员

保险评估员(访问者)评估不同类型的财产(房屋、汽车、珠宝),对每种财产计算保险费用,但评估标准因财产类型而异。

代码实现

Example.java

实际应用

Java NIO FileVisitor

Java NIO 中的 FileVisitor 接口是访问者模式的典型应用,用于遍历文件树并对每个文件/目录执行操作。

Example.java

Java ASM 字节码操作库

ASM 库使用访问者模式遍历和修改 Java 字节码,ClassVisitor、MethodVisitor 等都是访问者。

Example.java

Spring BeanDefinitionVisitor

Spring Framework 使用访问者模式遍历 Bean 定义并解析占位符。

Example.java

练习

1

访问者模式的核心机制是什么?

2

访问者模式的主要优点是什么?

3

在访问者模式中,当添加新的元素类型时,需要修改所有的访问者类。

4

以下哪个场景最适合使用访问者模式?