目录

  • 1 介绍
  • 2 例子
  • 2.1 可访问接口
  • 2.2 员工抽象类
  • 2.3 员工实现类
  • 2.4 访问者接口
  • 2.5 访问者实体
  • 2.6 测试主类
  • 3 例子升华
  • 4 总结
  • 4.1 核心角色
  • 4.2 核心代码结构



1 介绍

访问者模式实现对象数据数据操作的解耦。

2 例子

一个公司有程序员和产品经理,年末了,需要做考核,CEO和CTO关注员工的考核指标不一样,CEO关心KPI,CTO关心产品数和代码行数,如果把CEO考核和CTO考核方式都写在员工类里面,这样耦合就大了,于是访问者模式就横空出世了,我非常喜欢的两个设计模式之一(另一个是装饰者模式)。

2.1 可访问接口

//定义可访问的接口
interface Visitable{
    void accept(Visitor visitor);
}

里面定义了一个接待方法,接待的对象是一个访问者接口。

2.2 员工抽象类

abstract class Staff implements Visitable{
    public String name;//姓名
    public int kpi;// 员工KPI

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

2.3 员工实现类

//工程师
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);
    }
}
//产品经理
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);
    }
}

2.4 访问者接口

interface Visitor {
    // 访问工程师类型
    void visit(Engineer engineer);
    // 访问经理类型
    void visit(Manager manager);
}

访问者里面是一个重载访问方法,意思是不管CEO还是CTO都要访问所有的员工。思考:形参为什么不能直接只是一个staff即可?

2.5 访问者实体

//CEO
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());
    }
}
//CTO
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());
    }
}

2.6 测试主类

public class Main {
    public static void main(String[] args) {
        //主函数测试
        //先定义一些员工
        List<Staff> mStaffs = new ArrayList<Staff>();
        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"));

        //定义一个CEO
        CEOVisitor ceo=new CEOVisitor();
        System.out.println("---CEO开始访问---");
        for(Staff s:mStaffs){
            s.accept(ceo);
        }

        //CTO
        CTOVisitor cto=new CTOVisitor();
        System.out.println("---CTO开始访问---");
        for(Staff s:mStaffs){
            s.accept(cto);
        }
    }
}

运行结果:

---CEO开始访问---
经理: 经理-A, KPI: 2, 新产品数量: 5
工程师: 工程师-A, KPI: 0
工程师: 工程师-B, KPI: 7
工程师: 工程师-C, KPI: 0
经理: 经理-B, KPI: 4, 新产品数量: 9
工程师: 工程师-D, KPI: 1
---CTO开始访问---
经理: 经理-A, 产品数量: 2
工程师: 工程师-A, 代码行数: 19929
工程师: 工程师-B, 代码行数: 70946
工程师: 工程师-C, 代码行数: 58417
经理: 经理-B, 产品数量: 8
工程师: 工程师-D, 代码行数: 7040

3 例子升华

4 总结

4.1 核心角色

  • 可被访问接口:一个接收访问者(接口)的方法。
  • 被访问的实体类:实现可访问接口方法,方法形参是访问者(接口),方法体调用访问者的visit方法,把自己(对象)作为参数传进去。
  • 访问者接口:定义visit方法,方法形参是一个类,一般而言,访问者接口要非常了解被访问的对象。
  • 访问者实现:不同的访问者,在visit方法里面编写具体的访问逻辑(被访问实体是参数)。

4.2 核心代码结构

访问者模式的核心代码结构非常巧妙。是一个交叉的形式。

// 被访问者要实现的接口
interface Visitable{
    void accept(Visitor visitor);
}
//访问者要实现的接口
interface Visitor {
    void visit(visitableClass c);//形参是实现了Visitable的类
}

值得好好品味,被访问者只需要定义自己的数据,数据的操作交给访问者去实现,实现了数据结构和数据操作的解耦。