设计模式 | 访问者模式

792 阅读4分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第 1 天,点击查看活动详情 

Intent 意图

访问者模式建议将新行为放入一个名为访问者 的独立类中, 而不是试图将其整合到已有类中。 现在, 需要执行操作的原始对象将作为参数被传递给访问者中的方法, 让方法能访问对象所包含的一切必要数据。

Motivation 动机

当不同的节点类型所需要的节点操作不同时,或者是需要往一个稳定的类里面,需新增一些方法时,建议通过一个独立的访问者类,去新增行为。将访问者类或者方法作为参数传入给对应的节点类中,让访问者可以访问到节点类里面的必要数据,从而实现往节点类中新增不同的行为。

  1. 对象结构比较稳定,但经常需要在此对象结构上定义新的操作,非对象本身的必要操作
  2. 需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而需要避免这些操作“污染”这些对象的类,也不希望在增加新操作时修改这些类。

Applicability 适用范围

  1. 当需要对一个复杂对象进行遍历执行,且对象中每个元素的方法存在差异时,可以使用访问者模式
  2. 可使用访问者模式来清理辅助行为的业务逻辑。一类具体行为划分为一组,而非修改实际的对象。

Structure 结构

image.png

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 结构相似的节点,每个节点都可能执行不同的操作。

image.png

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:

(包括本文参考、或本文的截图及示例出处)

  1. refactoringguru.cn/design-patt…
  2. www.jianshu.com/p/1f1049d0a…