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

说明

访问者模式是一种行为型模式,表示一个作用于其对象结构中的各元素的操作,它使你可以在不改变各元素类的前提下定义作用于这些元素的新操作。
可以对定义这么理解:有这么一个操作,它是作用于一些元素(Element)之上的,而这些元素属于某一个对象结构(ObjectStructure)。同时这个操作是在不改变各元素类的前提下,在这个前提下定义新操作是访问者模式精髓中的精髓。

应用场景

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

模式特征

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

代码实现

代码场景:年底,CEO和CTO开始评定员工一年的工作绩效,员工分为工程师和经理,CTO关注工程师的代码量、经理的新产品数量;CEO关注的是工程师的KPI和经理的KPI以及新产品数量

  • 访问者抽象类
public interface Visitor {
    // 访问工程师类型
    void visit(Engineer engineer);

    // 访问经理类型
    void visit(Manager manager);
}
  • 具体访问者一
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());
    }
}
  • 具体访问者二
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());
    }
}
  • 元素抽象类
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);
    }
}
  • 对象结构类
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);
        }
    }
}
  • 客户端测试
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());
    }
}
  • 结果
=========== CEO看报表 ===========
经理: 经理-A, KPI: 1, 新产品数量: 5
工程师: 工程师-A, KPI: 7
工程师: 工程师-B, KPI: 9
工程师: 工程师-C, KPI: 7
经理: 经理-B, KPI: 8, 新产品数量: 0
工程师: 工程师-D, KPI: 7
=========== CTO看报表 ===========
经理: 经理-A, 产品数量: 6
工程师: 工程师-A, 代码行数: 7979
工程师: 工程师-B, 代码行数: 25806
工程师: 工程师-C, 代码行数: 8039
经理: 经理-B, 产品数量: 0
工程师: 工程师-D, 代码行数: 73291

优缺点

优点
  • 符合单一职责原则
  • 优秀的扩展性
  • 灵活性非常高
缺点
  • 具体元素对访问者公布细节,也就是说访问者关注了其他类的内部细节,这是迪米特法则所不建议的
  • 具体元素变更比较困难
  • 违背了依赖倒转原则。访问者依赖的是具体元素,而不是抽象元素

参考:https://www.jianshu.com/p/1f1049d0a0f4

原创文章 67 获赞 31 访问量 5万+

猜你喜欢

转载自blog.csdn.net/u012534326/article/details/102986121