设计模式轻松学【二六】访问者模式

简介: 现在短视频火爆,小A录制了一段视频发布在平台,每天都有很多访问,但是这些访问的人不确定,有的是朋友,有的是陌生人。

定义与特点

  • 定义:封装某些作用于某种数据结构中各元素的操作,它可以在不改变数据结构的前提下定义作用于这些元素的新的操作。
  • 详细概述

    • 访问者模式属于行为型模式。
    • 访问者模式是一种将数据结构和数据操作分离的设计模式。
    • 访问者模式比较复杂,而且实际使用的地方并不多。
    • 访问者模式适用于数据结构稳定的元素操作上,一旦数据结构易变,则不适用。
  • 参与角色

    • Visitor(抽象访问者):接口或者抽象类,为每一个元素(Element)声明一个访问的方法。
    • ConcreteVisitor(具体访问者):实现抽象访问者中的方法,即对每一个元素都有其具体的访问行为。
    • Element(抽象元素):接口或者抽象类,定义一个accept方法,能够接受访问者(Visitor)的访问。
    • ConcreteElement(具体元素):实现抽象元素中的accept方法,通常是调用访问者提供的访问该元素的方法。
    • Client(客户端类):即要使用访问者模式的地方。
  • 类结构图

    3-1Q119101429D6.gif

  • 结构代码示例

    • 定义抽象访问者

      interface Visitor {
          void visit(ConcreteElementA element);
      
          void visit(ConcreteElementB element);
      }
    • 定义具体访问者

      //具体访问者A类
      class ConcreteVisitorA implements Visitor {
          public void visit(ConcreteElementA element) {
              System.out.println("具体访问者A访问-->" + element.operationA());
          }
      
          public void visit(ConcreteElementB element) {
              System.out.println("具体访问者A访问-->" + element.operationB());
          }
      }
      
      //具体访问者B类
      class ConcreteVisitorB implements Visitor {
          public void visit(ConcreteElementA element) {
              System.out.println("具体访问者B访问-->" + element.operationA());
          }
      
          public void visit(ConcreteElementB element) {
              System.out.println("具体访问者B访问-->" + element.operationB());
          }
      }
    • 定义抽象元素

      interface Element {
          void accept(Visitor visitor);
      }
    • 定义具体元素

      //具体元素A类
      class ConcreteElementA implements Element {
          public void accept(Visitor visitor) {
              visitor.visit(this);
          }
      
          public String operationA() {
              return "具体元素A的操作。";
          }
      }
      
      //具体元素B类
      class ConcreteElementB implements Element {
          public void accept(Visitor visitor) {
              visitor.visit(this);
          }
      
          public String operationB() {
              return "具体元素B的操作。";
          }
      }
    • 定义对象结构角色

      class ObjectStructure {
          private List<Element> list = new ArrayList<Element>();
      
          public void accept(Visitor visitor) {
              Iterator<Element> i = list.iterator();
              while (i.hasNext()) {
                  ((Element) i.next()).accept(visitor);
              }
          }
      
          public void add(Element element) {
              list.add(element);
          }
      
          public void remove(Element element) {
              list.remove(element);
          }
      }
    • 客户端调用测试

      public class Client {
      
          public static void main(String[] args) {
              ObjectStructure os = new ObjectStructure();
              os.add(new ConcreteElementA());
              os.add(new ConcreteElementB());
              Visitor visitor = new ConcreteVisitorA();
              os.accept(visitor);
              System.out.println("------------------------");
              visitor = new ConcreteVisitorB();
              os.accept(visitor);
          }
      }
      
      //输出结果
      具体访问者A访问-->具体元素A的操作。
      具体访问者A访问-->具体元素B的操作。
      ------------------------
      具体访问者B访问-->具体元素A的操作。
      具体访问者B访问-->具体元素B的操作。

案例分析

我们都知道财务都是有账本的,这个账本就可以作为一个对象结构,而它其中的元素有两种,收入和支出,这满足我们访问者模式的要求,即元素的个数是稳定的,因为账本中的元素只能是收入和支出。

而查看账本的人可能有这样几种,比如老板,会计事务所的注会,财务主管,等等。而这些人在看账本的时候显然目的和行为是不同的。

  • 定义账单查看者(抽象访问者)

    interface AccountBookViewer {
        //查看消费的单子
        void view(ConsumeBill bill);
    
        //查看收入的单子
        void view(IncomeBill bill);
    }
  • 定义会计、老板(具体访问者)

    //注册会计师类,查看账本的类之一
    class CPA implements AccountBookViewer {
    
        // 注会在看账本时,如果是支出,则如果支出是工资,则需要看应该交的税交了没
        public void view(ConsumeBill bill) {
            if (bill.getItem().equals("工资")) {
                System.out.println("注会查看工资是否交个人所得税。");
            }
        }
    
        // 如果是收入,则所有的收入都要交税
        public void view(IncomeBill bill) {
            System.out.println("注会查看收入交税了没。");
        }
    }
    
    //老板类,查看账本的类之一
    class Boss implements AccountBookViewer {
    
        private double totalIncome;
    
        private double totalConsume;
    
        // 老板只关注一共花了多少钱以及一共收入多少钱,其余并不关心
        public void view(ConsumeBill bill) {
            totalConsume += bill.getAmount();
        }
    
        public void view(IncomeBill bill) {
            totalIncome += bill.getAmount();
        }
    
        public double getTotalIncome() {
            System.out.println("老板查看一共收入多少,数目是:" + totalIncome);
            return totalIncome;
        }
    
        public double getTotalConsume() {
            System.out.println("老板查看一共花费多少,数目是:" + totalConsume);
            return totalConsume;
        }
    }
  • 定义单子(抽象元素)

    interface Bill {
        void accept(AccountBookViewer viewer);
    }
  • 定义消费单子、收入单子(具体元素)

    //消费的单子
    class ConsumeBill implements Bill {
    
        private double amount;
    
        private String item;
    
        public ConsumeBill(double amount, String item) {
            this.amount = amount;
            this.item = item;
        }
    
        public void accept(AccountBookViewer viewer) {
            viewer.view(this);
        }
    
        public double getAmount() {
            return amount;
        }
    
        public String getItem() {
            return item;
        }
    }
    
    //收入单子
    class IncomeBill implements Bill {
    
        private double amount;
    
        private String item;
    
        public IncomeBill(double amount, String item) {
            this.amount = amount;
            this.item = item;
        }
    
        public void accept(AccountBookViewer viewer) {
            viewer.view(this);
        }
    
        public double getAmount() {
            return amount;
        }
    
        public String getItem() {
            return item;
        }
    }
  • 定义账本类(对象结构角色)

    class AccountBook {
        // 单子列表
        private List<Bill> billList = new ArrayList<Bill>();
    
        // 添加单子
        public void addBill(Bill bill) {
            billList.add(bill);
        }
    
        // 供账本的查看者查看账本
        public void show(AccountBookViewer viewer) {
            for (Bill bill : billList) {
                bill.accept(viewer);
            }
        }
    }
  • 客户端调用程序

    public class VisitorTest {
    
        public static void main(String[] args) {
            AccountBook accountBook = new AccountBook();
            //添加两条收入
            accountBook.addBill(new IncomeBill(10000, "卖商品"));
            accountBook.addBill(new IncomeBill(12000, "卖广告位"));
            //添加两条支出
            accountBook.addBill(new ConsumeBill(1000, "工资"));
            accountBook.addBill(new ConsumeBill(2000, "材料费"));
    
            AccountBookViewer boss = new Boss();
            AccountBookViewer cpa = new CPA();
    
            //两个访问者分别访问账本
            accountBook.show(cpa);
            accountBook.show(boss);
    
            ((Boss) boss).getTotalConsume();
            ((Boss) boss).getTotalIncome();
        }
    }

上面的代码中,账本以及账本中的元素是非常稳定的,这些几乎不可能改变,而最容易改变的就是访问者这部分。

访问者模式最大的优点就是增加访问者非常容易,我们从代码上来看,如果要增加一个访问者,你只需要做一件事即可,那就是写一个类,实现AccountBookViewer接口,然后就可以直接调用AccountBook的show方法去访问账本了。

如果没使用访问者模式,一定会增加许多if else,而且每增加一个访问者,你都需要改你的if else,代码会显得非常臃肿,而且非常难以扩展和维护。

总结

  1. 优点

    • 使得数据结构和作用于结构上的操作解耦,使得操作集合可以独立变化。
    • 添加新的操作或者说访问者会非常容易。
    • 将对各个元素的一组操作集中在一个访问者类当中。
    • 使得类层次结构不改变的情况下,可以针对各个层次做出不同的操作,而不影响类层次结构的完整性。
    • 可以跨越类层次结构,访问不同层次的元素类,做出相应的操作。
  2. 缺点

    • 增加新的元素会非常困难。
    • 实现起来比较复杂,会增加系统的复杂性。
    • 破坏封装,如果将访问行为放在各个元素中,则可以不暴露元素的内部结构和状态,但使用访问者模式的时候,为了让访问者能获取到所关心的信息,元素类不得不暴露出一些内部的状态和结构,就像收入和支出类必须提供访问金额和单子的项目的方法一样。
  3. 适用场景

    • 对象结构比较稳定,但经常需要在此对象结构上定义新的操作。
    • 需要对一个对象结构中的对象进行很多不同的且不相关的操作,而需要避免这些操作“污染”这些对象的类,也不希望在增加新操作时修改这些类。
目录
相关文章
|
6月前
|
设计模式 存储 uml
C++ 设计模式实战:外观模式和访问者模式的结合使用,派生类访问基类的私有子系统
C++ 设计模式实战:外观模式和访问者模式的结合使用,派生类访问基类的私有子系统
68 1
|
6月前
|
设计模式
二十三种设计模式全面解析-访问者模式的高级应用和实践技巧
二十三种设计模式全面解析-访问者模式的高级应用和实践技巧
|
1月前
|
设计模式 JavaScript 前端开发
JavaScript设计模式--访问者模式
【10月更文挑战第1天】
31 3
|
6月前
|
设计模式 算法 Java
【设计模式】JAVA Design Patterns——Acyclic Visitor(非循环访问者模式)
【设计模式】JAVA Design Patterns——Acyclic Visitor(非循环访问者模式)
|
2月前
|
设计模式 缓存 算法
Java设计模式-访问者模式(22)
Java设计模式-访问者模式(22)
|
6月前
|
设计模式 算法
设计模式 - 行为型模式_ 访问者模式Visitor Pattern
设计模式 - 行为型模式_ 访问者模式Visitor Pattern
73 1
设计模式 - 行为型模式_ 访问者模式Visitor Pattern
|
6月前
|
设计模式 安全 Java
【设计模式】字节三面:请举例阐释访问者模式
【设计模式】字节三面:请举例阐释访问者模式
44 2
|
设计模式 算法
行为型设计模式11-访问者模式
行为型设计模式11-访问者模式
46 1
|
6月前
|
设计模式 算法 Java
23种设计模式,访问者模式的概念优缺点以及JAVA代码举例
【4月更文挑战第10天】访问者模式是一种将算法与对象结构分离的设计模式。这种模式主要用于执行一个操作(或一组操作)在一个对象结构的各元素上,它可以在不修改各元素的类的前提下定义新的操作。
52 2
|
6月前
|
设计模式 Go
[设计模式 Go实现] 行为型~访问者模式
[设计模式 Go实现] 行为型~访问者模式

热门文章

最新文章

  • 1
    C++一分钟之-设计模式:工厂模式与抽象工厂
    42
  • 2
    《手把手教你》系列基础篇(九十四)-java+ selenium自动化测试-框架设计基础-POM设计模式实现-下篇(详解教程)
    46
  • 3
    C++一分钟之-C++中的设计模式:单例模式
    54
  • 4
    《手把手教你》系列基础篇(九十三)-java+ selenium自动化测试-框架设计基础-POM设计模式实现-上篇(详解教程)
    38
  • 5
    《手把手教你》系列基础篇(九十二)-java+ selenium自动化测试-框架设计基础-POM设计模式简介(详解教程)
    62
  • 6
    Java面试题:结合设计模式与并发工具包实现高效缓存;多线程与内存管理优化实践;并发框架与设计模式在复杂系统中的应用
    57
  • 7
    Java面试题:设计模式在并发编程中的创新应用,Java内存管理与多线程工具类的综合应用,Java并发工具包与并发框架的创新应用
    41
  • 8
    Java面试题:如何使用设计模式优化多线程环境下的资源管理?Java内存模型与并发工具类的协同工作,描述ForkJoinPool的工作机制,并解释其在并行计算中的优势。如何根据任务特性调整线程池参数
    50
  • 9
    Java面试题:请列举三种常用的设计模式,并分别给出在Java中的应用场景?请分析Java内存管理中的主要问题,并提出相应的优化策略?请简述Java多线程编程中的常见问题,并给出解决方案
    106
  • 10
    Java面试题:设计模式如单例模式、工厂模式、观察者模式等在多线程环境下线程安全问题,Java内存模型定义了线程如何与内存交互,包括原子性、可见性、有序性,并发框架提供了更高层次的并发任务处理能力
    78