访问者模式(Visitor Pattern)

2024/3/29 Java进阶

将数据操作与数据结构分离,将一些稳定的对象的类拥有一个accept方法用来接收访问者访问。

# 1、访问者模式案例

  • 年终时,公司高层会根据每个员工这一年做出的功劳发年终奖,对基层员工和对管理层员工发的奖金也是不一样。
  • 设计思路:为了简单模拟这个过程,我们把员工分为工程师和经理,公司高层分为CTO和CEO。CTO只关注代码行数和代码质量,CEO只关注业绩。

定义一个接受访问者的方法accept(Visitor v)方法。此类作为抽象元素类。

public abstract class Staff {
    public String name;
    public int kpi;

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

    public abstract void accept(Vistor vistor);  
}
1
2
3
4
5
6
7
8
9
10
11

比较稳定的数据结构,稳定的对象的类拥有一个accept方法用来接收访问者访问。

// 经理
public class Manager extends Staff {
    private int products; //产品数量
    public Manager(String name) {
        super(name);
        products = new Random().nextInt(10);
    }

    @Override
    public void accept(Vistor vistor) {
        vistor.vist(this);
    }

    public int getProducts() {
        return products;
    }
}

// 工程师
public class Engineer extends Staff {
    private int codeLines; // 代码行数

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

    @Override
    public void accept(Vistor vistor) {
        vistor.vist(this);
    }

    public int getCodeLines(){
        return codeLines;
    }
}
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

抽象访问者类

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

具体的访问者

// CEO访问者
public class CEOVistor implements Vistor {
    @Override
    public void vist(Engineer engineer) {
        System.out.println("工程师:" + engineer.name + ",KPI:" + engineer.kpi);
    }

    @Override
    public void vist(Manager manager) {
        System.out.println("经理:" + manager.name + ",KPI:" + manager.kpi 
                        + ",新产品数量:" + manager.getProducts());
    }
}

// CTO访问者
public class CTOVistor implements Vistor {
    @Override
    public void vist(Engineer engineer) {
        System.out.println("工程师:" + engineer.name + ",代码数量:" + engineer.getCodeLines());
    }

    @Override
    public void vist(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
24
25
26

将访问者和元素做一个关联。

public class BusinessReport {
    List<Staff> mStaffs = new ArrayList<>();

    public BusinessReport(){
        mStaffs.add(new Manager("王经理"));
        mStaffs.add(new Manager("陈经理"));
        mStaffs.add(new Engineer("Android开发者"));
        mStaffs.add(new Engineer("iOS开发者"));
        mStaffs.add(new Engineer("php开发者"));
    }

    public void showReport(Vistor vistor){
        for (Staff staff : mStaffs){
            staff.accept(vistor);
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

测试

public class ClientTest {
    public static void main(String[] args) {
        //构建报表
        BusinessReport businessReport = new BusinessReport();

        System.out.println("====给CEO看的报表====");
        businessReport.showReport(new CEOVistor());

        System.out.println("====给CTO看的报表====");
        businessReport.showReport(new CTOVistor());
    }
}

/**
 * 输出结果:
 * ====给CEO看的报表====
 * 经理:王经理,KPI:3,新产品数量:4
 * 经理:陈经理,KPI:8,新产品数量:5
 * 工程师:Android开发者,KPI:3
 * 工程师:iOS开发者,KPI:0
 * 工程师:php开发者,KPI:7
 * ====给CTO看的报表====
 * 经理:王经理,产品数量:4
 * 经理:陈经理,产品数量:5
 * 工程师:Android开发者,代码数量:20545
 * 工程师:iOS开发者,代码数量:60319
 * 工程师:php开发者,代码数量:91292
 * /
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

参考:https://www.cnblogs.com/twcat/p/16912767.html