设计模式--访问者模式

最复杂的设计模式,并且使用频率不高,《设计模式》的作者评价为:大多情况下,你不需要使用访问者模式,但是一旦需要使用它时,那就真的需要使用了。
访问者模式是一种将数据操作和数据结构分离的设计模式。

例子:

年底,CEO和CTO开始评定员工一年的工作绩效,员工分为工程师和经理,CTO关注工程师的代码量、经理的新产品数量;CEO关注的是工程师的KPI和经理的KPI以及新产品数量。由于CEO和CTO对于不同员工的关注点是不一样的,这就需要对不同员工类型进行不同的处理。
员工基类
// 员工基类
public abstract class Staff {

    public String name;
    public int kpi;// 员工KPI

    public Staff(String name) {
        this.name = name;
        kpi = new Random().nextInt(10);
    }
  
    // 核心方法,接受Visitor的访问
    public abstract void accept(Visitor visitor);
}
工程师类
// 工程师
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);
    }
}
经理类
// 经理
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);
    }
}
然后将这些员工添加到一个业务报表类中,公司高层可以通过该报表类的 showReport 方法查看所有员工的业绩
// 员工业务报表类
public class BusinessReport {

    // 所有员工
    private List<Staff> mStaffs = new LinkedList<>();

    public BusinessReport() {
        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"));
    }

    /**
     * 为访问者展示报表
     * @param visitor 公司高层,如CEO、CTO
     */
    public void showReport(Visitor visitor) {
        for (Staff staff : mStaffs) {
            staff.accept(visitor);
        }
    }
}
定义一个Visitor接口
public interface Visitor {

    // 访问工程师类型
    void visit(Engineer engineer);

    // 访问经理类型
    void visit(Manager manager);
}
定义CEO类,(CEO只关注KPI,新产品数量)
// CEO访问者
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());
    }
}
再添加一个CTO类,(CTO 关注工程师代码行数,及经理的产品数量)
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());
    }
}
最后Client用法:
public class Client {

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

这种模式需要被访问的类的数据结构趋向于稳定,不会大变,只是访问的操作上可能会变。

比如:可以在Engineer 和 Manager中单独添加方法,CTO和CEO的visit就能直接访问到,而不是在他们共同的父类中添加方法,导致Engineer中独有的方法,Manager也得实现(空方法);非常的不便。

本质是为了减少或者不改动Engineer和Manager,将之后的改动放到CTO和CEO中,也就是设计模式七大原则中的开闭原则,面向修改封闭,面向扩展开放

不过从上也可以看出,访问者模式耦合上是有些深的。所以才说最复杂的设计模式,并且使用频率不高
参考:https://www.jianshu.com/p/1f1049d0a0f4
posted @ 2021-09-30 19:17  明月照江江  阅读(33)  评论(0编辑  收藏  举报