一 引入
年底,CEO和CTO开始评定员工一年的工作绩效,员工分为工程师和经理,CTO关注工程师的代码量、经理的新产品数量;CEO关注的是工程师的KPI和经理的KPI以及新产品数量。
传统方式:
让员工的实现分别提供不同形式的考核方法,如果系统比较小还是ok的但是考虑系统增加越来越多新的功能时,对代码改动较大,违反了ocp原则,不利于维护
这里就可以引入访问者模式
访问模式是一种行为型模式,访问者模式的定义:“表示一个作用于某对象结构中的各元素的操作。它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作”。
二 访问者模式uml类图
- Visitor 是抽象访问者,为该对象结构中的ConcreteElement的每一个类声明一个visit操作
- ConcreteVisitor :是一个具体的访问值 实现每个有Visitor 声明的操作,是每个操作实现的部分.
- ObjectStructure 能枚举它的元素, 可以提供一个高层的接口,用来允许访问者访问元素
- Element 定义一个accept 方法,接收一个访问者对象
- ConcreteElement 为具体元素,实现了accept 方法
三 java代码实现
定义访问者及其实现(Visitor ConcreteVisitor )
public interface Visitor {
void visit(Manager manager);
void visit(Engineer engineer);
}
public class CeoVisitor implements Visitor{
@Override
public void visit(Manager manager) {
System.out.println("经理: " + manager.getName() + ", KPI: " + manager.getKpi());
}
@Override
public void visit(Engineer engineer) {
System.out.println("工程师: " + engineer.getName() + ", KPI: " + engineer.getKpi());
}
}
public class CtoVisitor implements Visitor{
@Override
public void visit(Manager manager) {
System.out.println("经理: " + manager.getName() + ", 产品数量: " + manager.getMangerNum());
}
@Override
public void visit(Engineer engineer) {
System.out.println("工程师: " + engineer.getName() + ", 代码行数: " + engineer.getCodeNum());
}
}
定义被访问者
@Data
public abstract class Staff {
private String name;
private Integer kpi;
public Staff(String name){
this.name=name;
kpi = new Random().nextInt(10);
}
/**
* 接受访问者
* @param visitor
*/
abstract void accept(Visitor visitor);
}
public class Engineer extends Staff{
public Engineer(String name) {
super(name);
}
@Override
void accept(Visitor visitor) {
visitor.visit(this);
}
public Integer getCodeNum(){
return new Random().nextInt(1000);
}
}
public class Manager extends Staff{
public Manager(String name) {
super(name);
}
@Override
void accept(Visitor visitor) {
visitor.visit(this);
}
public Integer getMangerNum(){
return new Random().nextInt(10);
}
}
定义ObjectStructure
public class BusinessReport {
private List<Staff> mStaffs = new LinkedList<>();
public BusinessReport() {
mStaffs.add(new Manager("经理-A"));
mStaffs.add(new Engineer("工程师-A"));
mStaffs.add(new Engineer("工程师-B"));
mStaffs.add(new Engineer("工程师-C"));
mStaffs.add(new Manager("经理-B"));
mStaffs.add(new Engineer("工程师-D"));
}
/**
* 为访问者展示报表
* @param visitor 公司高层,如CEO、CTO
*/
public void showReport(Visitor visitor) {
for (Staff staff : mStaffs) {
staff.accept(visitor);
}
}
}
客户端调用
public class Client {
public static void main(String[] args) {
BusinessReport businessReport = new BusinessReport();
businessReport.showReport(new CeoVisitor());
businessReport.showReport(new CtoVisitor());
}
}
四 访问者模式在源码中的应用
Java 7 版本后,Files 类提供了 walkFileTree() 方法,该方法可以很容易的对目录下的所有文件进行遍历,需要 Path、FileVisitor 两个参数。其中,Path 是要遍历文件的路径,FileVisitor 则可以看成一个文件访问器。
五 总结
优点:
- 访问者模式符合单一职责原则、让程序具有优秀的扩展性、灵活性非常高
- 访问者模式可以对功能进行统一,可以做报表、UI、拦截器与过滤器,适用于数据结构相对稳定的系统
缺点:
- 具体元素对访问者公布细节,也就是说访问者关注了其他类的内部细节,这是迪米特法则所不建议的, 这样造成了具体元素变更比较困难
- 违背了依赖倒转原则。访问者依赖的是具体元素,而不是抽象元素
- 因此,如果一个系统有比较稳定的数据结构,又有经常变化的功能需求,那么访问者模式就是比较合适的.