访问者模式

介绍

23中设计模式中最复杂的一个,使用频率并不高。

基本思想是:软件系统中拥有一个由许多对象构成的,比较稳定的对象结构,这些对象的类都拥有一个accept方法,用来接受访问这对象的访问。访问者是一个接口,他拥有一个visit方法,这个方法对访问到的对象结构中不同类型的元素做出不同的处理。在对象结构一次访问过程中,我们遍历整个对象结构,对每一个元素都实施accept方法,在每一个元素的accept方法中会调用访问者的visit方法,从而使访问者得以处理对象结构的每一个元素,我们可以针对对象结构设计不同的访问者类来完成不同的操作,达到区别对待的效果。

使用场景

  • 对象结构比较稳定,但是经常需要在此对象结构上定义新的操作;

  • 需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而需要避免这些操作”污染”这些对象的类,也不希望在增加新的操作的时候修改这些类。

角色介绍

  • Visitor 
  • ConcreteVisitor 
  • Element  
  • ElementA,ElementB 
  • ObjectStructure

Visitor           接口或者抽象类
ConcreteVisitor      具体的访问者
Element          元素接口或者抽象类
ElementA,ElementB   具体的元素类
ObjectStructure      对象结构

简单实现

年终奖评定,员工分为工程师跟经理,评定员工的分别是CEO跟CTO,CTO只关注工程师的代码量,经理的新产品数量,而CEO关注的是工程师的KPI和经理的KPI以及新产品数量。CEO跟CTO对不同员工的关注点不一样,这就需要对不同的员工做出不同的处。这时候就可以使用访问者模式了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public abstract class Staff {

public String name;

public int kpi;

public Staff(String name) {
this.name = name;
this.kpi = new Random().nextInt(10*10000);
}

public abstract void accept(Visitor visitor);

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class Engineer extends Staff {

public Engineer(String name) {
super(name);
}

@Override
public void accept(Visitor visitor) {
visitor.visit(this);
}

public int getCodeLines(){
return new Random().nextInt(10*10000);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class Manager extends Staff {


public Manager(String name) {
super(name);
}

@Override
public void accept(Visitor visitor) {
visitor.visit(this);
}

public int getProducts(){
return new Random().nextInt(10);
}

}
1
2
3
4
public interface Visitor {
public void visit(Engineer engineer);
public void visit(Manager manager);
}
1
2
3
4
5
6
7
8
9
10
11
public class CEOVisitor implements Visitor {
@Override
public void visit(Engineer engineer) {
System.out.println("工程师:"+engineer.name+",KPI:"+engineer.kpi);
}

@Override
public void visit(Manager manager) {
System.out.println("经理:"+manager.name+",KPI:"+manager.kpi+",新产品数量:"+manager.getProducts());
}
}
1
2
3
4
5
6
7
8
9
10
11
public class CTOVisitor implements Visitor {
@Override
public void visit(Engineer engineer) {
System.out.println("工程师:"+engineer.name+",代码行数:"+engineer.getCodeLines());
}

@Override
public void visit(Manager manager) {
System.out.println("经理:"+manager.name+",产品数量:"+manager.getProducts());
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class BusinessReport {

List<Staff> mStaffs = new LinkedList<>();

public BusinessReport(){

mStaffs.add(new Manager("王经理"));
mStaffs.add(new Engineer("Engineer-张"));
mStaffs.add(new Engineer("Engineer-王"));
mStaffs.add(new Engineer("Engineer-李"));
mStaffs.add(new Engineer("Engineer-赵"));


}


public void showReports(Visitor visitor){

for (Staff staff:mStaffs){
staff.accept(visitor);
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
public class Client {

public static void main(String[] args){

BusinessReport businessReport = new BusinessReport();
System.out.println("====给CEO看的报表====");
businessReport.showReports(new CEOVisitor());
System.out.println("====给CTO看的报表====");
businessReport.showReports(new CTOVisitor());
}

}

打印结果:

1
2
3
4
5
6
7
8
9
10
11
12
====给CEO看的报表====
经理:王经理,KPI:36916,新产品数量:6
工程师:Engineer-张,KPI:76036
工程师:Engineer-王,KPI:99138
工程师:Engineer-李,KPI:96511
工程师:Engineer-赵,KPI:61203
====给CTO看的报表====
经理:王经理,产品数量:6
工程师:Engineer-张,代码行数:11714
工程师:Engineer-王,代码行数:33446
工程师:Engineer-李,代码行数:6967
工程师:Engineer-赵,代码行数:82335

访问者模式最大的优点是增加访问者非常容易,如果要增加一个访问者,你创建一个实现了Visitor接口的类,然后实现两个visit函数来对不同的元素进行不同的操作,从而达到数据对象与数据操作相分离的效果。如果不使用访问者模式,而又想对不同的元素进行不同的操作,那么必定需要使用if-else语句和类型转换,这使得代码难以升级维护。此时,访问者模式的作用就体现出来了。

当前网速较慢或者你使用的浏览器不支持博客特定功能,请尝试刷新或换用Chrome、Firefox等现代浏览器