定义

可作用于对象结构中各个元素,在不改变各元素类的前提下,定义作用于这些元素新操作方法的一种行为型设计模式。

主要组成

抽象访问者(Visitor): 声明出对对象结构中每一个具体元素的访问方法visit,传入Concrete Element对象作为参数

具体访问者(Concrete Visitor): 实现各种visit方法,调用具体元素对象完成对应的各种操作

元素(Element): 定义出抽象accept方法,用于接收Visitor对象参数

具体元素(Concrete Element): 实现accept操作,该操作一般用于操作执行Visitor.visit方法,将自身作为参数传入

对象结构(Object structure): 各个元素构成的一个整体,提供能够让访问者访问所有元素的接口。可以是集合(比如List),或者是复合的类对象。

UML图

框架代码

抽象访问者: 需要依赖于具体的元素类,而不是抽象元素类,以避免if-else之类的嵌套和类型判断

public interface Visitor {void visit(ConcreteElementA elementA);void visit(ConcreteElementB elementB);
}
复制代码

具体访问者:

public class ConcreteVisitorA implements Visitor{@Overridepublic void visit(ConcreteElementA elementA) {// use elementA to do something}@Overridepublic void visit(ConcreteElementB elementB) {// use elementB to do something}
}public class ConcreteVisitorB implements Visitor{@Overridepublic void visit(ConcreteElementA elementA) {// use elementA to do something}@Overridepublic void visit(ConcreteElementB elementB) {// use elementB to do something}
}
复制代码

抽象元素(可能接口或者抽象类): 由各个具体元素类实现accept接口,用于使得Visitor依赖于具体元素类

public interface Element {void accept(Visitor visitor);
}
复制代码

具体元素:

public class ConcreteElementA implements Element{@Overridepublic void accept(Visitor visitor) {visitor.visit(this);}}public class ConcreteElementB implements Element{@Overridepublic void accept(Visitor visitor) {visitor.visit(this);}}
复制代码

Client中简单调用:

    List<Element> elements = new ArrayList<>();elements.add(new ConcreteElementA());elements.add(new ConcreteElementB());elements.add(new ConcreteElementA());ConcreteVisitorA concreteVisitorA = new ConcreteVisitorA();ConcreteVisitorB concreteVisitorB = new ConcreteVisitorB();for (Element element : elements) {element.accept(concreteVisitorA);element.accept(concreteVisitorB);}
复制代码

具体例子

公司对员工进行考核,员工存在工程师和产品经理,考核的评审有CEO和CTO,CTO只关注工程师的代码量和产品经理的新产品数量。而CEO则关注工程师的KPI和产品经理的KPI及新产品数量。从这边可以看出CEO和CTO对员工考核的关注点不一样,这就需要对员工类型进行处理,就可以用到访问者模式(员工分类结构也是很稳定不变的)。(来自<<Android源码设计模式解析与实践>>)

UML

代码

员工Staff:

public abstract class Staff {String name;public Staff(String name) {this.name = name;}public String getName() {return name;}public abstract int getKPI();public abstract void accept(Visitor visitor);
}
复制代码

工程师Engineer:

public class Engineer extends Staff{public Engineer(String name) {super(name);}@Overridepublic int getKPI() {return 9;}@Overridepublic void accept(Visitor visitor) {visitor.visit(this);}public int getCodeLines() {return 9999;}}
复制代码

产品经理Manager:

public class Manager extends Staff{public Manager(String name) {super(name);}@Overridepublic int getKPI() {return 10;}@Overridepublic void accept(Visitor visitor) {visitor.visit(this);}public int getProductCount() {return 5;}
}
复制代码

访问者Visitor:

public interface Visitor {void visit(Engineer engineer);void visit(Manager manager);
}
复制代码

CEO:

public class CEOVisitor implements Visitor{@Overridepublic void visit(Engineer engineer) {System.out.println("CEOVisitor 考核工程师"+ engineer.getName() + ",KPI:" + engineer.getKPI());}@Overridepublic void visit(Manager manager) {// TODO Auto-generated method stubSystem.out.println("CEOVisitor 考核产品经理"+ manager.getName() + ",KPI:" + manager.getKPI() + ",产品数:" + manager.getProductCount());}}
复制代码

CTO:

public class CTOVisitor implements Visitor{@Overridepublic void visit(Engineer engineer) {System.out.println("CTOVisitor 考核工程师"+ engineer.getName() + ",CodeLines:" + engineer.getCodeLines());}@Overridepublic void visit(Manager manager) {// TODO Auto-generated method stubSystem.out.println("CTOVisitor 考核产品经理"+ manager.getName() + ",产品数:" + manager.getProductCount());}
}
复制代码

BussinessReport(对应Object structure):

public class BussinessReport {List<Staff> staffs = new ArrayList<>();public BussinessReport() {staffs.add(new Engineer("小张"));staffs.add(new Manager("小王"));}public void showReport(Visitor visitor) {for (Staff staff : staffs) {staff.accept(visitor);}}
}
复制代码

Client调用:

//构建报表
BussinessReport bussinessReport = new BussinessReport();
//给CEO看
bussinessReport.showReport(new CEOVisitor());
//给CTO看
bussinessReport.showReport(new CTOVisitor());
复制代码

假设不使用访问者模式

如果不使用访问者模式的话,也就不存在Visitor继承体系,以上述具体例子为例则需要对Staff的各种类型进行判断: 假设在ReportUtils处理:

public class ReportUtils {public static void visitCEO(Staff staff) {if (staff instanceof Manager) {Manager manager = (Manager)staff;System.out.println("考核产品经理"+ manager.getName() + ",产品数:" + manager.getProductCount());} else if(staff instanceof Engineer) {Engineer engineer = (Engineer)staff;System.out.println("考核工程师"+ engineer.getName() + ",KPI:" + engineer.getKPI());}}public static void visitCTO(Staff staff) {if (staff instanceof Manager) {Manager manager = (Manager)staff;System.out.println("考核产品经理"+ manager.getName() + ",产品数:" + manager.getProductCount());} else if(staff instanceof Engineer) {Engineer engineer = (Engineer)staff;System.out.println("考核工程师"+ engineer.getName() + ",CodeLines:" + engineer.getCodeLines());}}
}
复制代码

这就会导致出现一大堆的if -else嵌套和类型转换,当类型较多的时候就会难以扩展和维护,并且当关注层面不同,重复的判断会很多,难以维护后续持续扩展新的操作(visitXX,visitXXX...)。而使用访问者Visitor模式,能够通过accept-visit方法的配套使用使得不同关注面的操作可以分离,并且去除一大堆嵌套if-else和类型转换的判断,灵活性更好,可扩展性高,更易于维护。

总结

优点

角色职责分离,符合单一职责原则;

允许对组合结构中的各个元素加入新的操作,而不需要修改结构本身,增加新操作相对容易;

集中管理访问者所进行操作的代码;

缺点

具体元素元素对访问者公布细节,违反了迪米特原则(一个类对自己需要耦合或调用的类知道的最少);

增加删减具体元素时修改成本过大,需要修改Visitor继承体系;

为了达到"区别对待"而依赖具体类而不是抽象,违反了"依赖倒置"原则(高层模块不应该依赖低层模块,两者都应该依赖抽象);

应用场景

1、对象结构中对象对应的类很少改变,但经常需要在此对象结构上定义新的操作。

2、需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而需要避免让这些操作"污染"这些对象的类,也不希望在增加新操作时修改这些类。 注意事项:访问者可以对功能进行统一,可以做报表、UI、拦截器与过滤器。

微信公众号

小白设计模式:访问者模式相关推荐

  1. 设计模式 访问者模式

    文章目录 访问者模式 访问者模式实战 访问者模式 在相同的数据结构下, 增加容易变化的业务访问逻辑, 为了增强扩展性, 将易变的访问逻辑进行解耦的一种设计模式. 访问者模式实战 模拟学校中, 有老师和 ...

  2. 设计模式---访问者模式

    访问者模式 介绍 定义及使用场景 UML类图 角色 财务案例 个人心得体会 静态分派以及动态分派 静态分派 动态分派 访问者模式中的伪动态双分派 对访问者模式的一些思考 总结 优点 缺点 适用性 参考 ...

  3. C++设计模式——访问者模式(visitor pattern)

    一.原理讲解 1.1意图 表示一个作用于某对象结构中的各元素的操作.它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作. 1.2应用场景 一个对象结构包含很多类对象,它们有不同的接口,而你 ...

  4. java设计模式---访问者模式

      Java深入到一定程度,就不可避免的碰到设计模式这一概念,了解设计模式,将使自 己对java中的接口或抽象类应用有更深的理解.设计模式在java的中型系统中应用广 泛,遵循一定的编程模式,才能使自 ...

  5. yii2通过url访问类中的方法_行为型设计模式 访问者模式

    author zong email zongzhe1996@163.com 介绍 在访问者模式中,通过使用一个访问者类,可以改变元素类(被访问者)的执行算法.元素类的执行算法可以随着访问者的改变而改变 ...

  6. C++设计模式-访问者模式

    目录 基本概念 代码与实例 基本概念 访问者模式(Visitor):表示一个作用于某对象结构中的各元素的操作.它使你可以在不改变元素的类的前提下定义作用于这些元素的新操作. UML图如下(此图来源于大 ...

  7. PHP设计模式——访问者模式

    声明:本系列博客参考资料<大话设计模式>,作者程杰. 访问者模式表示一个作用于某对象结构中的各元素的操作.它使你可以在不改变各元素类的前提下定义作用于这些元素的新操作. UML类图: 角色 ...

  8. C#设计模式——访问者模式(Vistor Pattern)

    一.引言 在上一篇博文中分享了责任链模式,责任链模式主要应用在系统中的某些功能需要多个对象参与才能完成的场景.在这篇博文中,我将为大家分享我对访问者模式的理解. 二.访问者模式介绍 2.1 访问者模式 ...

  9. 大话设计模式—访问者模式

    在访问者模式(Visitor Pattern)中,我们使用了一个访问者类,它改变了元素类的执行算法.通过这种方式,元素的执行算法可以随着访问者改变而改变.这种类型的设计模式属于行为型模式.根据模式,元 ...

  10. C++设计模式——访问者模式

    访问者模式 在GOF的<设计模式:可复用面向对象软件的基础>一书中对访问者模式是这样说的:表示一个作用于某对象结构中的各元素的操作.它使你可以在不改变各元素的类的前提下定义作用于这些元素的 ...

最新文章

  1. 隔行如隔山 -- 乱弹技术,经济,对日外包
  2. mysql-视图、触发器、事务、存储过程、流程控制
  3. 2015年山石网科面试题
  4. Linux下进程的建立
  5. 把汇集多个json文件的txt进行分割,然后批量修改文件名后缀
  6. 数字电路中的建立时间与保持时间
  7. [Object]面向对象编程(高程版)(二)原型模式
  8. 报表如何同步用户数据集 1
  9. c语言怎样存放学生信息,C语言共用体存放学生信息
  10. android 多线程 加锁,android 多线程 — 从一个小例子再次品位多线程
  11. 悟空学Linux专栏----第3篇
  12. 北京天通苑二房东、黑中介
  13. 现在PayPal还可以怎样提现???账户只有91刀!
  14. SDN交换机配置说明—微云
  15. Linux 设备模型基本概念 (一)
  16. Cortex-M4架构
  17. layUi框架入门篇(一)
  18. 计算机的应用采用进位制,为什么计算机采用二进位制运算?
  19. if else的常见错误
  20. proxmox VE 7.0安装调试方法汇总

热门文章

  1. WebRTC直播技术方案
  2. 4K视频直播与点播系统的搭建与体验
  3. mysql无法添加或更新子行_MYSQL:错误:无法添加或更新子行:外键约束失败
  4. NYOJ-部分和问题(dfs)
  5. ubuntu16.04下安装windows软件,以及卸载.
  6. python将json数据集转成voc xml文件
  7. 支持javascript的博客汇总
  8. 多项目Node版本控制
  9. [Java] ArrayList、LinkedList、Vector的区别
  10. EBay架构案例分析