携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第 1 天,点击查看活动详情
Intent 意图
访问者模式建议将新行为放入一个名为访问者 的独立类中, 而不是试图将其整合到已有类中。 现在, 需要执行操作的原始对象将作为参数被传递给访问者中的方法, 让方法能访问对象所包含的一切必要数据。
Motivation 动机
当不同的节点类型所需要的节点操作不同时,或者是需要往一个稳定的类里面,需新增一些方法时,建议通过一个独立的访问者类,去新增行为。将访问者类或者方法作为参数传入给对应的节点类中,让访问者可以访问到节点类里面的必要数据,从而实现往节点类中新增不同的行为。
- 对象结构比较稳定,但经常需要在此对象结构上定义新的操作,非对象本身的必要操作。
- 需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而需要避免这些操作“污染”这些对象的类,也不希望在增加新操作时修改这些类。
Applicability 适用范围
- 当需要对一个复杂对象进行遍历执行,且对象中每个元素的方法存在差异时,可以使用访问者模式
- 可使用访问者模式来清理辅助行为的业务逻辑。一类具体行为划分为一组,而非修改实际的对象。
Structure 结构
reference:
Participate 结构成员
结构中各个类、对象所扮演的角色
- Visitor | 访问者: 定义一个 visitor 中所需要的接口方法
- ConcreteVisitor | 具体访问者: 具体实现一个 visitor 的行为方法
- Element | 元素接口: 定义一个元素提供给 visitor 的接口方法,定义了实际访问者可以访问的成员或者方法
- ConcreteElement | 具体元素: 具体实现元素接口,在实际使用过程中,通过将 visitor 作为参数传入,并在里面执行 vistor 里定义的接口方法,把自身引用传递给 visitor ,实现在 visitor 中访问具体元素具体的成员 or 方法
Cooperation 协作
- visitor 获取对应 element 的引用,并进行相应操作
- element 将 visitor 作为函数参数,并在内部执行 visitor 暴露给 elment 调用的方法,并将自己的 this 引用传递给 visitor
Consequence 后果
- Good
开闭原则: 你可以引入在不同类对象上执行的新行为, 且无需对这些类做出修改。通过引入访问者模式,仅修改访问者,而无需修改原来稳定的元素类的对象结构。
单一职责原则: 可将同一行为的不同版本移到同一个类中。
- Bad
当遇到需要访问元素类原本为私有成员对象的时候,由于接口的限制,需要将其公开,这会破坏原本元素类的封装性
Implementation 示例
interface Visitor {
visit(el: Element): void
}
interface Element {
property: any
accept(v: Visitor): void
}
Known uses 已知应用
- 遍历访问一个 Dom tree 结构相似的节点,每个节点都可能执行不同的操作。
Related Pattern 和其他模式直接的关系
访问者 & 命令模式
- 两者的结构相似,甚至连实现方式都很接近;
- 访问者模式可以是作为命令模式的加强版,其对象可以对不同层次的对象进行访问。
Code
interface Visitor {
visit(el: Element): void
}
interface Element {
name: string
accept(v: Visitor): void
}
class ConcreteElement implements Element {
name: string = 'No visited'
private age: number = 0
constructor(name: string = '') {
if (!!name) {
this.name = name
}
}
// key method 及时上 el.accept 调用的是 visitor 里的行为方法
accept(v: Visitor): void {
v.visit(this)
}
}
class ConcreteVisitor implements Visitor {
// key method
visit(el: Element): void {
console.log(el.name)
el.name = 'Visited'
console.log(el.name)
}
}
function client() {
const visitor = new ConcreteVisitor()
const element = new ConcreteElement('No')
element.accept(visitor) // key method,将 visitor 对象传入 element 进行调用
}
client()
往期文章 index
【设计模式索引】 juejin.cn/post/705899…
Reference:
(包括本文参考、或本文的截图及示例出处)