23种设计模式之访问者模式

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

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

使用场景
a. 对象结构比较稳定,但经常需要在此对象结构上定义新的操作
b. 需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而需要避免这些操作“污染”这些对象的类,也不希望在增加新操作时修改这些类。

UML类图
这里写图片描述

角色介绍:
Visitor:接口或者抽象类,它定义了对每一个元素访问的行为,它的参数就是可以访问的元素,它的方法个数理论上来讲与元素个数是一样的,因此,访问者模式要求元素的类族要稳定,如果经常添加、移除元素类,必然会导致频繁的修改Visitor接口,如果出现这种情况,则说明不适合使用访问者模式。
ConcreteVisitor:具体的访问者,它需要给出对每一个元素类访问时所产生的具体行为。
Element:元素接口或者抽象类,它定义了一个接受访问者的方法,其意义是指每一个元素都要可以被访问者访问。
ElementA、ElementB:具体的元素类,它提供接受访问方法的具体实现,而这个具体的实现,通常情况下是使用访问者提供的访问该元素类的方法。
ObjectStructure:定义当中所提到的对象结构,对象结构是一个抽象表述,它内部管理了元素集合,并且可以迭代这些元素供访问者访问。

源码实现

//员工基类
public abstract class Staff {
    public String name;
    //员工kpi
    public int 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 {
    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;
    }
}

// 员工业务报表1类
public class BusinessReport {
    List<Staff> mStaffs = new LinkedList<Staff>();

    public BusinessReport() {
        mStaffs.add(new Manager("米经理"));
        mStaffs.add(new Engineer("工程师-jia"));
        mStaffs.add(new Engineer("工程师-wei"));
        mStaffs.add(new Engineer("工程师-wang"));
        mStaffs.add(new Engineer("工程师-shi"));
    }

    /**
     * 为访问者展示报表
     * @param visitor
     */
    public void showReport(Visitor visitor){
        for (Staff staff : mStaffs){
            staff.accept(visitor);
        }
    }
}

//访问者接口
public interface Visitor {
    //访问工程师类型
    public void visit(Engineer engineer);
    //访问经理类型
    public void visit(Manager leader);
}

//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 leader) {
        System.out.println("经理:" + leader.name + ", KPI:" + leader.kpi + ",新产品数量:" + leader.getProducts());
    }
}

public class CTOVisitor implements Visitor {
    @Override
    public void visit(Engineer engineer) {
        System.out.println("工程师:"+engineer.name+",代码函数:"+engineer.getCodeLines());
    }

    @Override
    public void visit(Manager leader) {
        System.out.println("经理:"+leader.name+",产品数量:"+leader.getProducts());
    }
}

//客户端代码
public class Client {
    public static void main(String[] args){
        //构建报表
        BusinessReport report = new BusinessReport();
        System.out.println("==========给CEO看的报表=========");
        //设置访问者,这里是CEO
        report.showReport(new CEOVisitor());
        System.out.println("==========给CTO看的报表=========");
        //注入另一个访问者CTO
        report.showReport(new CTOVisitor());
    }
}

输出结果:

==========给CEO看的报表=========
经理:米经理, KPI:4,新产品数量:5
工程师:工程师-jia,KPI:3
工程师:工程师-wei,KPI:0
工程师:工程师-wang,KPI:1
工程师:工程师-shi,KPI:3
==========给CTO看的报表=========
经理:米经理,产品数量:5
工程师:工程师-jia,代码函数:3442
工程师:工程师-wei,代码函数:70417
工程师:工程师-wang,代码函数:47537
工程师:工程师-shi,代码函数:49825

总结
大多说情况下,你并不需要使用访问者模式,但是,当你一旦需要使用它时,那你就是真的需要它了。在现实情况下,我们要根据具体的情况来评估是否适合使用访问者模式。在使用一个模式之前,一定要明确它的使用场景、他能解决什么问题,以此来避免滥用设计模式的现象。
访问者模式的优点:
a. 各角色职责分离、符合单一职责原则
b. 具有优秀的扩展性
c. 使得数据结构和作用于结构上的操作解耦,使得操作集合可以独立变化
d. 灵活性
访问者模式的缺点:
a. 具体元素对访问者公布细节,违反了迪米特原则
b. 具体元素变更时导致修改成本大
c. 违反了依赖倒置原则,为了达到“区别对待”而依赖了具体类,没有依赖抽象。

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值