访问者模式(Visitor Pattern)是一种将数据结构与数据操作分离的设计模式,指封装一些作用于某种数据结构中的各元素的操作,可以在不改变数据结构的前提下定义作用于这些元素的新的操作,属于行为型设计模式。

    场景

    • 需要遍历很多不同的实现的对象,并且遍历的过程中对不同类型的对象有不同的处理,替代使用instanceof的做法
    • 需要数据结构与数据操作分离
    • 被访问者类型稳定

    伪动态双分派,accept一次visit一次

    • 重载 静态绑定 编译期决定 参数的类型
    • 覆写 动态绑定 运行时决定 调用者的类型

    java静态多分派动态单分派

    image.png

    1. 抽象访问者(IVisitor):接口或抽象类,该类定义了一个visit()方法用于访问每一个具体的元素,其参数就是具体的元素对象。从理论上来说,IVisitor的方法个数与元素种类个数是相等的。如果元素种类个数经常变动,则导致IVisitor的方法也要进行变动,此时,该情形并不适用于访问者模式。
    2. 具体访问者(ConcreteVisitor):实现对具体元素的操作。
    3. 抽象元素(IElement):接口或抽象类,定义了一个接受访问者访问的方法accept(),表示所有元素类型都支持被访问者访问。
    4. 具体元素(ConcreteElement):具体元素类型,提供接受访问者的具体实现。通常的实现都为visitor.visit(this)。
    5. 结构对象(ObjectStructure):该类内部维护了元素集合,并提供方法接受访问者对该集合所有元素进行操作。
    1. public interface IElement {
    2. void accept(IVisitor visitor);
    3. }
    4. public interface IVisitor {
    5. void visit(ElementA element);
    6. void visit(ElementB element);
    7. }
    8. public class ElementA implements IElement {
    9. @Override
    10. public void accept(IVisitor visitor) {
    11. visitor.visit(this);
    12. }
    13. public void operationA(){
    14. }
    15. }
    16. public class ElementB implements IElement {
    17. @Override
    18. public void accept(IVisitor visitor) {
    19. //visitor第二次动态分派,this静态分派
    20. visitor.visit(this);
    21. }
    22. public void operationB(){
    23. }
    24. }
    25. public class ConcreteVisitorA implements IVisitor {
    26. @Override
    27. public void visit(ElementA element) {
    28. element.operationA();
    29. }
    30. @Override
    31. public void visit(ElementB element) {
    32. element.operationB();
    33. }
    34. }
    35. public class ConcreteVisitorB implements IVisitor {
    36. @Override
    37. public void visit(ElementA element) {
    38. }
    39. @Override
    40. public void visit(ElementB element) {
    41. }
    42. }
    43. public class Client {
    44. public static void main(String[] args){
    45. IVisitor visitorA = new ConcreteVisitorA();
    46. IVisitor visitorB = new ConcreteVisitorB();
    47. List<IElement> list = new ArrayList<>();
    48. for(IElement element : list){
    49. //第一次动态分派
    50. element.accept(visitorA);
    51. element.accept(visitorB);
    52. }
    53. }
    54. }

    优点

    • 解耦数据结构与数据操作,通过扩展类型的访问者实现对元素集的不同操作
    • 增加访问者简单,符合开闭原则

    缺点

    • 访问者依赖具体元素而不是抽象元素,具体元素的修改会影响每个访问者,同时访问者需要关注具体元素的实现细节
    • 违法依赖倒置原则
    • 增加或者修改被访问者困难