设计模式之访问者模式

设计模式之访问者模式

访问者模式是一种复杂的行为模式。表示要对对象结构的元素执行的操作。访问者允许对对象结构中的节点定义新操作,而无需更改其操作的元素的类。

适用于数据结构相对未定的系统,它把数据结构和作用于结构上的操作之间的耦合解脱开,使得操作集合可以相对自由地演化。

应用场景

当遇到如下情况时,可以优先考虑使用访问者模式:

  1. 对象结构中包含许多具有不同接口的对象类,并且希望对依赖于其具体类的这些对象执行操作。
  2. 需要对对象结构中的对象执行许多不同且不相关的操作,并且希望避免使用这些操作“污染”它们的类。这时就可以定义多个visitor类来完成对多个对象节点的处理。
  3. 定义对象结构的类很少改变,但是你经常想要在结构上定义新的操作。

模式结构

举个例子:在公司中,需要统计打卡记录和绩效,分别有行政部门和财务部门来实现。并且将来可能有质量部门监测研发和实施人员的工作质量。但是对研发和实施人员的考核标准有所不同。

一、类图

类图

OperateStructure作为对象OperateNode的数据结构,并且实现类OperateNodeAOperateNodeB行为方法有各自的实现。并且想基于这个相对固定的数据结构实现多种不同的操作(visitor)。这样在增加visitor的时候不必考虑数据结构会发生变更。

二、时序

由于调用过程比较不容易看透,这里我先使用时序图来说明调用顺序

调用过程时序

代码实例

根据类图可以看出,访问者模式分为五部分:抽象节点角色 、具体节点角色 、抽象访问者角色 、具体访问者角色 、结构对象角色

  • 抽象节点角色:
1
2
3
4
public abstract class OperateNode {
// 定义节点内都需要实现的方法
abstract void 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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
// 具体节点A 在实例中对应研发人员
class OperateNodeA extends OperateNode {
//绩效系数
private static final double COEFFICIENT = 1.2;

private int workDate;

OperateNodeA(int workDate) {
this.workDate = workDate;
}

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

int attendance(){
return workDate;
}

double performance(){
return workDate * COEFFICIENT;
}
}
// 具体节点B,在实例中对应运维人员
class OperateNodeB extends OperateNode {

private static final int COEFFICIENT = 1;

private int workDate;

OperateNodeB(int workDate) {
this.workDate = workDate;
}

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

int attendance(){
return workDate;
}

double performance(){
return workDate * COEFFICIENT;
}
}
  • 抽象访问者角色
1
2
3
4
5
//针对结构中每一个节点分别进行各自的操作
public interface Visitor {
void visit(OperateNodeA operateNodeA);
void visit(OperateNodeB operateNodeB);
}
  • 具体访问者角色
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
// 行政人员结算考勤
public class VisitorA implements Visitor {
// 需要对实施人员增加路上的考勤时间 模式为2
private static final int BASIC = 2;
@Override
public void visit(OperateNodeA operateNodeA) {
int att = operateNodeA.attendance();
System.out.println("本月该研发人员考勤为"+att + "天");
}

@Override
public void visit(OperateNodeB operateNodeB) {
int att =operateNodeB.attendance() + BASIC;
System.out.println("本月该实施人员考勤为"+att + "天");
}
}
//财务人员结算绩效
public class VisitorB implements Visitor {
// 研发需要添加额外绩效
private static final int EXTRA = 2;
@Override
public void visit(OperateNodeA operateNodeA) {
double att = operateNodeA.performance() + EXTRA;
System.out.println("本月该研发人员绩效为 :"+att );
}

@Override
public void visit(OperateNodeB operateNodeB) {
double att =operateNodeB.performance() ;
System.out.println("本月该研发人员绩效为 :"+att );
}
}
  • 结构对象结构
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class OperateStructure {
// 定义结构体
private List<OperateNode> list = new ArrayList<>();

void add(OperateNode operateNode){
list.add(operateNode);
}
//对结构体中的对象实现接收访问操作
void action(Visitor visitor){
for (OperateNode operateNode : list){
// 目的就是使用双重分派调用来实现
// 节点接受访问者与访问者访问节点的操作
operateNode.accept(visitor);
}
}
}
  • 客户端调用
1
2
3
4
5
6
7
8
9
10
11
public class App {
public static void main(String[] args) {
Visitor visitorA = new VisitorA();
Visitor visitorB = new VisitorB();
OperateStructure structure = new OperateStructure();
structure.add(new OperateNodeA(22));
structure.add(new OperateNodeB(22));
structure.action(visitorA);
structure.action(visitorB);
}
}
  • 结果
1
2
3
4
本月该研发人员考勤为22天
本月该实施人员考勤为24天
本月该研发人员绩效为 :28.4
本月该研发人员绩效为 :22.0
-------------本文结束感谢您的阅读-------------

本文标题:设计模式之访问者模式

文章作者:NanYin

发布时间:2019年06月15日 - 12:06

最后更新:2019年08月12日 - 13:08

原始链接:https://nanyiniu.github.io/2019/06/15/2019-06-15-%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%E4%B9%8B%E8%AE%BF%E9%97%AE%E8%80%85%E6%A8%A1%E5%BC%8F/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。