个人博客

http://www.milovetingting.cn

访问者模式

模式介绍

访问者模式是一种将数据操作与数据结构分离的设计模式,它是《设计模式》中23种设计模式最复杂的一个,但它的使用频率不高。

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

模式定义

封装一些作用于某种数据结构中的各元素的操作,它可以在不改变这个数据结构的前提下定义作用于这些元素的新的操作。

使用场景

  1. 对象结构比较稳定,但经常需要在此对象结构上定义新的操作。

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

简单使用

定义Staff

1
2
3
4
5
6
7
8
9
10
11
12
public abstract class Staff {
public String name;
public int kpi;

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

public abstract void accept(Visitor visitor);
}

定义Visitor

1
2
3
4
public interface Visitor {
public void visit(Engineer engineer);
public void visit(Manager manager);
}

定义Staff实现类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
public class Engineer extends Staff {

private int codeLines;

public Engineer(String name) {
super(name);
codeLines = new Random().nextInt(10 * 1000);
}

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

public int getCodeLines() {
return codeLines;
}

}

public class Manager extends Staff{

private int products;

public Manager(String name) {
super(name);
products = new Random().nextInt(10);
}

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

public int getProducts(){
return products;
}

}

定义Report

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class Report {
List<Staff> staffs = new ArrayList<Staff>();

public Report() {
staffs.add(new Manager("王经理"));
staffs.add(new Engineer("王工"));
staffs.add(new Engineer("李工"));
staffs.add(new Engineer("周工"));
}

public void showReport(Visitor visitor) {
for (Staff staff : staffs) {
staff.accept(visitor);
}
}
}

定义Visitor实现类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
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());
}

}

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
public class Main {

public static void main(String[] args) {
Report report = new Report();
System.out.println("=====给CEO看的报表=====");
report.showReport(new CEOVisitor());
System.out.println("=====给CTO看的报表=====");
report.showReport(new CTOVisitor());
}

}

输出结果

1
2
3
4
5
6
7
8
9
10
=====给CEO看的报表=====
经理:王经理,KPI:7,产品数量:5
工程师:王工,KPI:7
工程师:李工,KPI:7
工程师:周工,KPI:6
=====给CTO看的报表=====
经理:王经理,产品数量:5
工程师:王工,代码行数:1818
工程师:李工,代码行数:4146
工程师:周工,代码行数:8558

小结

优点

  1. 各角色职责分离,符合单一职责原则

  2. 具有优秀的扩展性

  3. 使得数据结构和作用于结构上的操作解耦,使得操作可以独立变化

4.灵活性

缺点

  1. 具体元素对访问者公布细节,违反了迪米特原则

  2. 具体元素变更时导致修改成本大

  3. 违反了依赖倒置原则,为了达到”区别对待”而依赖了具体类,没有依赖抽象