友情支持

如果您觉得这个笔记对您有所帮助,看在D瓜哥码这么多字的辛苦上,请友情支持一下,D瓜哥感激不尽,😜

支付宝

微信

有些打赏的朋友希望可以加个好友,欢迎关注D 瓜哥的微信公众号,这样就可以通过公众号的回复直接给我发信息。

wx jikerizhi

公众号的微信号是: jikerizhi因为众所周知的原因,有时图片加载不出来。 如果图片加载不出来可以直接通过搜索微信号来查找我的公众号。

25. 访问者模式

25.1. 定义

根据 GoF 的著名著作 《设计模式》,访问者模式的定义如下:

访问者模式(Visitor)

表示 一个作用于某对象结构中的各元素的操作。它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作。

— Erich Gamma、Richard Helm、Ralph Johnson、John Vlissides
《设计模式》

25.2. 类图

Diagram

男人这本书的内容要比封面吸引人,女人这本书的封面通常是比内容更吸引人。 男人的青春表示一种肤浅,女人的青春标志一种价值。

访问者模式讲的是表示一个作用于某对象结构中的各元素的操作。它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作。

一种双分派的技术,首先在客户程序中将具体状态作为参数传递给“男人”类完成了一次分派,然后“男人”类调用作为参数的“具体状态”中的方法“男人反应”,同时将自己(this)作为参数传递进去。这便完成了第二次分派。双分派意味着得到执行的操作决定于请求的种类和两个接收者的类型。‘接受’方法就是一个双分派的操作,它得到执行的操作不仅决定于‘状态’类的具体状态,还决定于它访问的‘人’的类别。

访问者模式适用于数据结构相对稳定的系统

它把数据结构和作用于结构上的操作之间的耦合解脱开,使得操作集合可以相对自由地演化。

访问者模式的目的是要把处理从数据结构分离出来。很多系统可以按照算法和数据结构分开,如果这样的系统有比较稳定的数据结构,又有易于变化的算法的话,使用访问者模式就是比较合适的,因为访问者模式使得算法操作的增加变得容易。反之,如果这样的系统的数据结构对象易于变化,经常要有新的数据对象增加进来,就不适合使用访问者模式。

访问者模式的优点就是增加新的操作很容易,因为增加新的操作就意味着增加一个新的访问者。访问者模式将有关的行为集中到一个访问者对象中。

通常ConcreteVisitor可以单独开发,不必跟ConcreteElementA或ConcreteElementB写在一起。正因为这样,ConcreteVisitor能提高ConcreteElement之间的独立性,如果把一个处理动作设计成ConcreteElementA和ConcreteElementB类的方法,每次想新增“处理”以扩充功能时就得去修改ConcreteElementA和ConcreteElementB了。

那访问者的缺点其实也就是使增加新的数据结构变得困难了。

GoF四人中的一个作者就说过:‘大多时候你并不需要访问者模式,但当一旦你需要访问者模式时,那就是真的需要它了。’

Visitor类,为该对象结构中ConcreteElement的每一个类声明一个Visit操作。

ConcreteVisitor1和ConcreteVisitor2类,具体访问者,实现每个由Visitor声明的操作。每个操作实现算法的一部分,而该算法片断乃是对应于结构中对象的类。

Element类,定义一个Accept操作,它以一个访问者为参数。

ConcreteElementA和ConcreteElementB类,具体元素,实现Accept操作。

ObjectStructure类,能枚举它的元素,可以提供一个高层的接口以允许访问者访问它的元素。

访问者模式的能力和复杂性是把双刃剑,只有当你真正需要它的时候,才考虑使用它。

代码 143. visitor/Client.java 类
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
package com.diguage.didp.visitor;

/**
 * Client 类
 *
 * @author D瓜哥, https://www.diguage.com/
 * @since 2017-05-19 17:53:36
 */
public class Client {
  public static void main(String[] args) {
    ObjectStructure o = new ObjectStructure();
    o.attach(new ConcreteElementA());
    o.attach(new ConcreteElementB());

    ConcreteVisitor1 v1 = new ConcreteVisitor1();
    ConcreteVisitor2 v2 = new ConcreteVisitor2();

    o.accept(v1);
    o.accept(v2);
  }
}
代码 144. visitor/ConcreteElementA.java 类
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
package com.diguage.didp.visitor;

/**
 * ConcreteElementA 类
 *
 * @author D瓜哥, https://www.diguage.com/
 * @since 2017-05-19 17:53:36
 */
public class ConcreteElementA extends Element {
  @Override
  public void accept(Vistor vistor) {
    vistor.visitConcreteElementA(this);
  }

  public void operatorA() {
    System.out.println("B其他操作");
  }
}
代码 145. visitor/ConcreteElementB.java 类
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
package com.diguage.didp.visitor;

/**
 * ConcreteElementB 类
 *
 * @author D瓜哥, https://www.diguage.com/
 * @since 2017-05-19 17:53:36
 */
public class ConcreteElementB extends Element {
  @Override
  public void accept(Vistor vistor) {
    vistor.visitConcreteElementB(this);
  }

  public void operatorB() {
    System.out.println("B其他操作");
  }
}
代码 146. visitor/ConcreteVisitor1.java 类
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
package com.diguage.didp.visitor;

/**
 * ConcreteVisitor1 类
 *
 * @author D瓜哥, https://www.diguage.com/
 * @since 2017-05-23 09:09:50
 */
public class ConcreteVisitor1 extends Vistor {
  @Override
  public void visitConcreteElementA(ConcreteElementA element) {
    System.out.printf("%s 被 %s 访问%n", //
        element.getClass().getName(), this.getClass().getName());
  }

  @Override
  public void visitConcreteElementB(ConcreteElementB element) {
    System.out.printf("%s 被 %s 访问%n", //
        element.getClass().getName(), this.getClass().getName());
  }
}
代码 147. visitor/ConcreteVisitor2.java 类
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
package com.diguage.didp.visitor;

/**
 * ConcreteVisitor2 类
 *
 * @author D瓜哥, https://www.diguage.com/
 * @since 2017-05-23 09:09:50
 */
public class ConcreteVisitor2 extends Vistor {
  @Override
  public void visitConcreteElementA(ConcreteElementA element) {
    System.out.printf("%s 被 %s 访问%n", //
        element.getClass().getName(), this.getClass().getName());
  }

  @Override
  public void visitConcreteElementB(ConcreteElementB element) {
    System.out.printf("%s 被 %s 访问%n", //
        element.getClass().getName(), this.getClass().getName());
  }
}
代码 148. visitor/Element.java 类
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
package com.diguage.didp.visitor;

/**
 * Element 类
 *
 * @author D瓜哥, https://www.diguage.com/
 * @since 2017-05-19 17:53:36
 */
public abstract class Element {
  public abstract void accept(Vistor vistor);
}
代码 149. visitor/ObjectStructure.java 类
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package com.diguage.didp.visitor;

import java.util.ArrayList;
import java.util.List;

/**
 * ObjectStructure 类
 *
 * @author D瓜哥, https://www.diguage.com/
 * @since 2017-05-19 17:53:36
 */
public class ObjectStructure {
  private List<Element> elements = new ArrayList<>();

  public void attach(Element element) {
    elements.add(element);
  }

  public void detach(Element element) {
    elements.remove(element);
  }

  public void accept(Vistor vistor) {
    elements.forEach(e -> e.accept(vistor));
  }
}
代码 150. visitor/Vistor.java 类
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
package com.diguage.didp.visitor;

/**
 * Vistor 类
 *
 * @author D瓜哥, https://www.diguage.com/
 * @since 2017-05-23 09:09:50
 */
public abstract class Vistor {
  public abstract void visitConcreteElementA(ConcreteElementA element);

  public abstract void visitConcreteElementB(ConcreteElementB element);
}