visitor模式要求元素类型稳定且visitor接口预先覆盖全部类型,每个元素类实现accept()精准分发至对应visit方法,新增操作只需添加concretevisitor,结构负责遍历、访问者决定处理逻辑。

可以直接加新访问者类,不用动任何已有元素类——但前提是元素类型已稳定、Visitor接口已覆盖全部类型。
Visitor接口必须提前写全所有元素类型
Java中不能用泛型或运行时判断代替显式方法声明。每个具体元素类型都要在接口里配一个对应方法:
- 比如结构里有Paragraph、Image、Table,接口就得定义三个方法:
visit(Paragraph p)、visit(Image i)、visit(Table t) - 漏掉任意一个,新访问者就无法安全处理该类型;后续若新增Video,就必须改接口——所有已存在的访问者实现都得补
visit(Video v),反而破坏扩展性 - 方法名体现“谁被访问”,不是“要干什么”。写
visit(Image),不写visitForExport()或visitForThumbnail(),否则每次业务变化都会倒逼接口重构
每个元素类只做一件事:把调用精准分发过去
accept()方法是硬编码的跳转点,不能靠反射、字符串拼接或条件判断:
-
Paragraph.accept(Visitor v)里直接写v.visit(this),确保调到visit(Paragraph) -
Image.accept(Visitor v)里也只写v.visit(this),调到visit(Image) - 别在
accept()里做递归遍历(比如Directory自己遍历子节点),那是访问者的职责;accept()体越简单越好,只负责一次分发
新增操作 = 新建ConcreteVisitor实现全部visit方法
比如要加「导出为Markdown」功能,只需新建一个类:
- 实现
Visitor接口,补全visit(Paragraph)、visit(Image)、visit(Table)三个方法 - 每个方法内只用元素公开的字段和方法(如
paragraph.getText()、image.getSrc()),不能访问私有成员 - 如果发现某个计算逻辑被多个访问者重复使用(比如拼接完整路径),建议在元素类上加个
getFullPath()这样的只读方法,而不是让每个访问者自己拼
对象结构负责遍历,访问者决定怎么处理
像文档树、AST或文件系统这类复合结构,本身只需提供统一入口:
- 定义
Document或Directory类,内部持有List<element></element> - 提供
accept(Visitor v)方法,遍历所有子元素并调用各自accept(v) - 访问者拿到整个结构后,自行控制遍历顺序、是否递归、是否跳过某些节点——比如「拼写检查」访问者可忽略
Image,「图片提取」访问者只处理Image
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











