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

简介: 现在短视频火爆,小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. 适用场景

    • 对象结构比较稳定,但经常需要在此对象结构上定义新的操作。
    • 需要对一个对象结构中的对象进行很多不同的且不相关的操作,而需要避免这些操作“污染”这些对象的类,也不希望在增加新操作时修改这些类。
相关文章
|
1月前
|
设计模式 存储 uml
C++ 设计模式实战:外观模式和访问者模式的结合使用,派生类访问基类的私有子系统
C++ 设计模式实战:外观模式和访问者模式的结合使用,派生类访问基类的私有子系统
29 1
|
4月前
|
设计模式
二十三种设计模式全面解析-访问者模式的高级应用和实践技巧
二十三种设计模式全面解析-访问者模式的高级应用和实践技巧
|
6月前
|
设计模式 算法 C++
设计模式之访问者模式(C++)
设计模式之访问者模式(C++)
|
3月前
|
设计模式 算法
设计模式 - 行为型模式_ 访问者模式Visitor Pattern
设计模式 - 行为型模式_ 访问者模式Visitor Pattern
39 1
设计模式 - 行为型模式_ 访问者模式Visitor Pattern
|
1月前
|
设计模式 算法 Java
【设计模式】访问者模式
【设计模式】访问者模式
|
6月前
|
设计模式 算法
行为型设计模式11-访问者模式
行为型设计模式11-访问者模式
26 1
|
3月前
|
设计模式 Java 容器
聊聊Java设计模式-访问者模式
访问者模式(Visitor Pattern)指将作用域某种数据结构中的各元素的操作分离出来封装成独立的类,使其在不改变数据结构的前提下可以添加作用于这些元素的新的操作。
28 3
|
3月前
|
设计模式 Go 开发工具
Golang设计模式——09访问者模式
Golang设计模式——09访问者模式
410 0
|
3月前
|
设计模式 前端开发 数据处理
【设计模式】之访问者模式
访问者模式是一种非常有用的设计模式,在前端开发中经常用于处理复杂对象结构和数据集合。它通过将操作和数据结构分离开来,提供了一种优雅而灵活的方式来处理复杂性。通过使用访问者模式,我们可以提高代码的可维护性和可扩展性。然而,在应用访问者模式时需要权衡其带来的优缺点,并根据具体情况进行选择。
34 0
|
4月前
|
设计模式 存储
二十三种设计模式全面解析-揭秘访问者模式:开启对象间灵活交互之门
二十三种设计模式全面解析-揭秘访问者模式:开启对象间灵活交互之门