1. 模式定义

  • 1)访问者模式(Visitor Pattern),封装一些作用于某种数据结构的各元素的操作,它可以在不改变数据结构的前提下定义作用于这些元素的新的操作
  • 2)主要将数据结构与数据操作分离,解决数据结构和操作耦合性问题
  • 3)访问者模式的基本工作原理是:在被访问的类里面加一个对外提供接待访问者的接口%%
    0631-🏡⭐️◼️访问者模式的概念和实现方法🔜📝 封装一些作用于数据元素的操作,还可以在不改变数据结构的前提下定义与数据元素的其他操作;主要意义在于将数据和操作分离;实现方法是在数据结构上增加一个对外访问的接口◼️⭐️-point-202301300631%%

2. 模式结构

2.1. UML

image.png

2.2. 模式角色

  • Visitor(抽象访问者):为该对象结构中每个ConcreteElement类声明一个visit操作,它的参数就是可以访问的元素,它的方法个数理论上来讲与元素类个数(Element的实现类个数)是一样的,从这点不难看出,访问者模式要求元素类的个数不能改变
  • ConcreteVisitor(具体访问者):实现Visitor中声明的操作,即给出对每一个元素类访问时所产生的具体行为
  • Element(抽象元素):定义一个accept方法,接受一个访问者对象,其意义是指,每一个元素都要可以被访问者访问。
  • ConcreteElement(具体元素):实现了accept方法,而这个具体的实现,通常情况下是使用访问者提供的访问该元素类的方法。
  • ObjectStructure(对象结构):能枚举它的元素,提供一个高层接口,允许访问者访问元素。定义当中所提到的对象结构,对象结构是一个抽象表述,具体点可以理解为一个具有容器性质或者复合对象特性的类,它会含有一组元素(Element),并且可以迭代这些元素,供访问者访问。

2.3. 实现逻辑⭐️🔴

  1. ObjectStructure(对象结构)聚合Element(抽象元素),并用集合管理起来,用遍历的方式,在遍历中调用Element的accept方法;对外提供高层接口,入参为ConcreteVisitor(具体访问者)
  2. ConcreteElement(具体元素)是Element的实现类,实现了accept方法,接收入参为Visitor(抽象访问者)
  3. Client 在调用 ObjectStructure (对象结构) 提供的高层接口时,传入 ConcreteVisitor (具体访问者),在遍历中传给 Element (抽象元素) 的 accept 方法 ❕%%
    0651-🏡⭐️◼️访问者模式的核心逻辑是什么?🔜📝 1. ObjectStructure聚合并用集合管理Element,在遍历过程中调用Element的accept方法,方法入参为具体的Visitor,是用Client调用ObjectStructure提供的高层访问接口时传入的。2. ConcreteElement继承或实现Element,实现accept方法,接收入参为Visitor,在accept方法中调用ConcreteVisitor的feed(this)方法,入参为当前ConcreteElement◼️⭐️-point-202301300651%%

3. 案例分析

3.1. 给宠物喂食

  • 访问者角色:给宠物喂食的人
  • 具体访问者角色:主人、其他人
  • 抽象元素角色:动物抽象类
  • 具体元素角色:宠物狗、宠物猫
  • 结构对象角色:主人家

3.1.1. UML

3.1.2. 实现逻辑

  1. Client创建对象结构类Home聚合抽象元素类Animal,并在类用集合管理Animal的子类

  2. Client调用Home提供的高层接口时传入具体的访问者类Owner
    image.png

  3. 在对象结构类Home中遍历执行Animal的accept方法,并传入Client提供的具体访问者类Owner
    image.png

  4. 在具体Animal类中调用accept方法时,实际调用的是传入的具体访问者类Owner的feed方法
    image.png

3.1.3. Demo

design_patterns: [[pages/002-schdule/001-Arch/001-Subject/013-DemoCode/design_patterns/src/main/java/com/itheima/pattern/visitor/Home.java]]

3.2. 测评系统⭐️🔴

3.2.1. UML

image.png

3.2.2. 实现逻辑

3.2.3. Demo

DesignPattern:[[ObjectStructure.java]]

4. 双分派

该例中我们使用到了双分派

  • 第一次分派:首先在客户端程序ObjectStructure中,将具体状态作为参数传递Woman
    image.png

image.png

  • 第二次分派:然后Woman类调用作为参数的具体方法getWomanResult,同时将自己this作为参数传入
    image.png

image.png

所谓双分派是指不管类怎么变化,我们都能找到期望的方法运行
双分派意味着得到执行的操作取决于请求的种类和两个接收者的类型
以上述实例为例,假设我们要添加一个Wait的状态类,考察Man类和Woman类的反应
由于使用了双分派,只需增加一个 Action 子类即可在客户端调用即可,不需要改动任何其他类的代码 ❕%%
0707-🏡⭐️◼️访问者模式的双分派是什么意思🔜📝 Client访问ObjectStructure提供的高层接口时,遍历调用Element的accept方法,此时Element会动态选择ConcreteElement,此为第一次分派;在accept方法中,ConcreteVisitor调用feed方法,会根据传入的this动态选择入参,此为第二次分派◼️⭐️-point-202301300707%%

5. 适用场景⭐️🔴

  • 对象结构相对稳定,但其操作算法经常变化的程序,比如投票系统中抽象 Action 和 Person 是相对稳定的,但是他们的实现却会不断扩展
  • 对象结构中的对象需要提供多种不同且不相关的操作,而且要避免让这些操作的变化“污染”对象的结构,即让数据结构与数据操作分离。

6. 优缺点

优点

  • 扩展性好
    在不修改对象结构中的元素的情况下,为对象结构中的元素添加新的功能。
  • 复用性好
    通过访问者来定义整个对象结构通用的功能,从而提高复用程度。
  • 分离无关行为
    通过访问者来分离无关的行为,把相关的行为封装在一起,构成一个访问者,这样每一个访问者的功能都比较单一

缺点

  • 对象结构变化很困难
    在访问者模式中,每增加一个新的元素类,都要在每一个具体访问者类中增加相应的具体操作,这违背了“开闭原则”
  • 违反了依赖倒置原则
    访问者模式依赖了具体类,而没有依赖抽象类。

7. JDK源码分析

8. 实战经验

9. 参考与感谢

设计模式-2、设计模式及设计原则