定义

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

结构和说明

行为型模式-访问者模式 - 图1

示例代码

  1. public class VisitorDemo {
  2. /**
  3. * 访问者接口
  4. */
  5. public static interface Visitor {
  6. /**
  7. * 访问元素 A,相当于给元素 A 添加访问者的功能
  8. *
  9. * @param elementA
  10. * 元素 A 的对象
  11. */
  12. void visitConcreteElementA(ConcreteElementA elementA);
  13. /**
  14. * 访问元素 B,相当于给元素 B 添加访问者的功能
  15. *
  16. * @param elementB
  17. * 元素 B 的对象
  18. */
  19. void visitConcreteElementB(ConcreteElementB elementB);
  20. }
  21. /**
  22. * 具体的访问者实现
  23. */
  24. public static class ConcreteVisitor1 implements Visitor {
  25. @Override
  26. public void visitConcreteElementA(ConcreteElementA elementA) {
  27. // 把要访问 ConcreteElementA 时,需要执行的功能实现在这里
  28. // 可能需要访问元素已有的功能,比如:
  29. elementA.operationA();
  30. }
  31. @Override
  32. public void visitConcreteElementB(ConcreteElementB elementB) {
  33. elementB.operationB();
  34. }
  35. }
  36. public static class ConcreteVisitor2 implements Visitor {
  37. @Override
  38. public void visitConcreteElementA(ConcreteElementA elementA) {
  39. elementA.operationA();
  40. }
  41. @Override
  42. public void visitConcreteElementB(ConcreteElementB elementB) {
  43. elementB.operationB();
  44. }
  45. }
  46. /**
  47. * 被访问的元素的接口
  48. */
  49. public static abstract class Element {
  50. /**
  51. * 接受访问者的访问
  52. *
  53. * @param visitor
  54. * 访问者对象
  55. */
  56. abstract void accept(Visitor visitor);
  57. }
  58. /**
  59. * 具体元素的实现对象
  60. */
  61. public static class ConcreteElementA extends Element {
  62. @Override
  63. void accept(Visitor visitor) {
  64. // 回调访问者对象的相应方法
  65. visitor.visitConcreteElementA(this);
  66. }
  67. /**
  68. * 示例方法,表示元素已有的功能实现
  69. */
  70. public void operationA() {
  71. // 已有的功能实现
  72. }
  73. }
  74. public static class ConcreteElementB extends Element {
  75. @Override
  76. void accept(Visitor visitor) {
  77. visitor.visitConcreteElementB(this);
  78. }
  79. public void operationB() {}
  80. }
  81. /**
  82. * 对象结构,通常在这里对元素对象进行遍历,让访问者能访问到所有元素
  83. */
  84. public static class ObjectStructure {
  85. /**
  86. * 示意,表示对象结构,可以是一个组合结构或是集合
  87. */
  88. private Collection<Element> col = new ArrayList<>();
  89. /**
  90. * 示意方法,提供给客户端操作的高层接口
  91. *
  92. * @param visitor
  93. * 客户端需要使用的访问者
  94. */
  95. public void handleRequest(Visitor visitor) {
  96. col.stream().forEach(element -> {
  97. element.accept(visitor);
  98. });
  99. }
  100. /**
  101. * 示意方法,组建对象结构,向对象结构中添加元素 不同对象结构有不同的构建方式
  102. *
  103. * @param ele
  104. * 加入对象结构的元素
  105. */
  106. public void addElement(Element ele) {
  107. col.add(ele);
  108. }
  109. }
  110. public static class Client {
  111. public static void main(String[] args) {
  112. // 创建 ObjectStructure
  113. ObjectStructure os = new ObjectStructure();
  114. // 创建要加入对象结构的元素
  115. Element elementA = new ConcreteElementA();
  116. Element elementB = new ConcreteElementB();
  117. // 把元素加入对象结构
  118. os.addElement(elementA);
  119. os.addElement(elementB);
  120. // 创建访问者
  121. Visitor visitor1 = new ConcreteVisitor1();
  122. // 调用业务处理的方法
  123. os.handleRequest(visitor1);
  124. }
  125. }
  126. }

调用顺序

行为型模式-访问者模式 - 图2

访问者模式结合组合模式

结构和说明

行为型模式-访问者模式 - 图3

优缺点

优点

  • 好的扩展性

能够在不修改对象结构中元素的情况下,为对象结构中的元素添加新的功能。

  • 好的复用性

可以通过定义访问者来定义整个对象结构中通用的功能,从而提高复用程度。

  • 分离无关行为

可以通过提供访问者来分离无关的行为,把相关的行为封装在一起,构成一个访问者,这样每个访问者的功能都比较单一。

缺点

  • 对象结构变化很困难

不适用于对象结构中的类经常变化的情况,因为对象结构发生了变化,访问者的结构和访问者的实现都要发生相应的变化,代价太高。

  • 破坏封装

访问者模式通常需要对象结构开放内部数据给访问者和 ObjectStructrue, 这破坏了对象的封装性。

思考

本质

预留通路,回调实现。

何时选用

  • 如果想对一个对象结构实施一些依赖于对象结构中具体类的操作,可以使用访问者模式。
  • 如果想对一个对象结构中的各个元素进行很多不同的而且不相关的操作,为了避免这些操作使类变得混乱,可以使用访问者模式。把这些操作分散到不同的访问者对象中去,每个访问者对象实现同一类的功能。
  • 如果对象结构很少变动,但是需要经常给对象结构中的元素对象定义新的操作,可以使用访问者模式。

相关模式

  • 访问者模式和组合模式

这两个模式可以组合使用。
如果前面示例的那样,通过访问者模式给组合对象预留下扩展功能的接口,使得为组合模式的对象结构添加功能非常方便。

  • 访问者模式和装饰模式

这两个模式从表面上看功能有些类似,都能够实现在不修改对象原结构的情况下修改原对象的功能。但是装饰模式更多的实现是对已有功能的加强、修改或者是完全全新实现; 而访问者模式更多的是实现为对象结构添加新的功能。

  • 访问者模式和解析器模式

这两个模式可以组合使用。
解释器模式在构建抽象语法树的时候,是使用组合模式来构建的,也就是说解析器模式解释并执行的抽象语法树是一个组合对象结构,这个组合对象结构是很少变动的,但是可能经常需要为解释器增加新的功能,实现对同一对象结构的不同解释和执行的功能,这正式访问者的优势所在,因此在使用解释器模式的时候通常会组合访问者模式来使用。