1. 为什么我们需要访问者模式
在软件开发中,我们经常会遇到这样的场景:随着业务发展,数据对象需要不断增加新的操作或行为。比如一个文档对象,最初可能只需要渲染和打印功能,后来陆续增加了导出PDF、生成水印、内容分析等需求。如果直接在对象内部添加这些方法,会导致几个严重问题:
- 违反单一职责原则 - 对象变得臃肿,承担了过多不相关的职责
- 破坏开闭原则 - 每次新增操作都需要修改对象源码
- 难以维护 - 当对象类型很多时,修改会波及整个类层次结构
访问者模式的核心思想是将数据结构和操作分离,把操作定义在独立的访问者对象中。这样当需要新增操作时,只需添加新的访问者,而不必修改原有数据结构。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 访问者模式的工作原理
2.1 基本结构
访问者模式包含两个核心组件:
- 元素(Element):定义accept方法,接受访问者对象
- 访问者(Visitor):为每种元素类型提供visit方法
典型类图如下:
code复制interface Element {
accept(visitor: Visitor): void;
}
class ConcreteElementA implements Element {
accept(visitor: Visitor) {
visitor.visitConcreteElementA(this);
}
}
interface Visitor {
visitConcreteElementA(element: ConcreteElementA): void;
visitConcreteElementB(element: ConcreteElementB): void;
}
class ConcreteVisitor1 implements Visitor {
visitConcreteElementA(element: ConcreteElementA) {
// 对ConcreteElementA的操作实现
}
}
2.2 双分派机制
访问者模式利用了双分派(double dispatch)技术:
- 第一次分派:在元素上调用accept方法,将自身(this)传递给访问者
- 第二次分派:访问者根据元素类型调用对应的visit方法
这种机制使得运行时能够确定具体要执行哪个操作,而不需要在元素类中使用条件判断。
3. 实战案例:文档处理系统
让我们通过一个文档处理系统的例子来演示访问者模式的实际应用。
3.1 基础元素定义
首先定义文档元素接口和具体元素类:
typescript复制interface DocumentElement {
accept(visitor: DocumentVisitor): void;
}
class TextElement implements DocumentElement {
constructor(public content: string) {}
accept(visitor: DocumentVisitor) {
visitor.visitText(this);
}
}
class ImageElement implements DocumentElement {
constructor(public src: string, public alt: string) {}
acce
