Android开发Android开发经验谈Android技术知识

访问者模式的最新详解

2020-05-13  本文已影响0人  不正经的创作者

介绍

访问者模式是一种将数据操作与数据结构分离的设计模式,它是 《设计模式》中较为复杂的一个,但它的使用频率并不高,正如《设计模式》的作者 GOF 对访问者模式的描述:大多数情况下,你并不需要使用访问者模式,但是当你一旦需要使用它时,那你就是真正的需要它了。

访问者模式的基本思想是,软件系统中拥有一个由许多对象构成的、比较稳定的对象结构,这些对象的类都拥有一个 accept 方法用来接受访问者对象的访问。访问者是一个接口,它拥有一个 visit 方法,这个方法对访问到的对象结构中不同类型的元素做出不同的处理。在对象结构的一次访问过程中,我们遍历整个对象结构,对每一个元素都实施 accept 方法,在每一个元素的 accept 方法中会调动访问者的 visit 方法,从而使访问者得到以处理对象结构的每一个元素,我们可以针对对象结构设计不同的访问者类来完成不同的操作,达到区别对待的效果。

定义

使用场景

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

UML 类图

代码示例

简单示例

需求: 公司的领导层年底对员工进行绩效考核,但是每个领导对于员工的关注点不同,所以要不同的访问处理。

员工抽象类

  public abstract class Staff {
      public String  name;

      /**
       * 员工 kpi
       */
      public int kpi;

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

      /**
       * 接受公司领导层对员的访问
       * @param visitor
       */
      public abstract  void accept(Visitor visitor);
  }

Staff 类定义了员工的基本信息及一个 accept 方法,表示接受公司领导的访问,由子类具体实现,下面来看看,具体实现。

程序员:

  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);
      }

      /**
       * 一年做的产品数量
       * @return
       */
      public int getProducts(){
          return new Random().nextInt(10);
      }

  }

在程序员、经理类中添加了各自的实现,因为他们的差异性才能使得访问者模式发挥它的工作。Staff、Engineer、Manager 这 3 个类就是对象结构,这些类相对稳定,不会发生变化。

员工报表:

  public class BusinesssReport {

      private List<Staff> mStaff = new ArrayList<>();

      public BusinesssReport(){
          mStaff.add(new Manager("DevYK 经理"));
          mStaff.add(new Engineer("IOS 工程师"));
          mStaff.add(new Engineer("Android 工程师"));
          mStaff.add(new Engineer("Java 工程师"));
          mStaff.add(new Engineer("C++ 工程师"));
      }

      /**
       * 为访问者展示报表
       */
      public void showReport(Visitor visitor){
          for (Staff staff : mStaff) {
              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: " + engineer.name + " kpi: " + 
  engineer.kpi + "]");
      }

      @Override
      public void visit(Manager leader) {
          System.out.println("leader = [" + "name: " + leader.name + " kpi: " + 
  leader.kpi + " 产品数量:" + leader.getProducts() + "]");

      }
  }

CTO 访问员工实现:

  public class CTOVisitor implements Visitor {
      @Override
      public void visit(Engineer engineer) {
          System.out.println("engineer = [" + engineer.name + " 代码行 
 数:"+engineer.getCodeLines()+"]");
      }

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

Client 客户端代码 --》年总进行访问:

      @Test
      public void textVisitor(){
          //构建报表
          BusinesssReport businesssReport = new BusinesssReport();
          //给 CEO 汇报的报表
          System.out.println(">>>>>>>给 CEO 汇报的报表");
          businesssReport.showReport(new CEOVisitor());
          //给 CTO 汇报的报表
          System.out.println(">>>>>>>给 CTO 汇报的报表");
          businesssReport.showReport(new CTOVisitor());
    }

客户端代码中,首先构建了一个报表对象,该对象中维护了所有员工的集合,然后通过报表类的 showReport 函数为 Visitor 对象提供一个访问接口,在这个函数中查看所有员工信息,然后调用员工的 accept 函数接受公司领导层的访问,每个访问者对不同职位的员工调用对应的 visit 函数实现不同的操作。绩效如下:

ouput:

  >>>>>>>给 CEO 汇报的报表
  leader = [name: DevYK 经理 kpi: 6 产品数量:6]
  engineer = [name: IOS 工程师 kpi: 3]
  engineer = [name: Android 工程师 kpi: 1]
  engineer = [name: Java 工程师 kpi: 3]
  engineer = [name: C++ 工程师 kpi: 9]

  >>>>>>>给 CTO 汇报的报表
  leader = [DevYK 经理 产品数量:4]
  engineer = [IOS 工程师 代码行数:81491]
  engineer = [Android 工程师 代码行数:25627]
  engineer = [Java 工程师 代码行数:61337]
  engineer = [C++ 工程师 代码行数:51714]

在上述示例中, Staff 扮演了 Element 角色,而 Engineer 和 Manager 都是 ConcreteElement; CEOVisitor 和 CTOVisitor 都是具体的访问者 Visitor 对象实现;而 BusinessReport 就是 ObjectStructure; Client 就是客户端代码,详细结构可以下下面的 UML 类图

总结

优点:

缺点:

在使用该模式之前,我们应该明确它的使用场景、它能解决什么问题等,以此来避免滥用设计模式。所以,在学习设计模式时,一定要理解模式的适用性及优缺点。

上一篇 下一篇

猜你喜欢

热点阅读