• 设计模式Design Pattern(4) 访问者模式


    什么是访问者模式?

      一个对象有稳定的数据结构,却为不同的访问者提供不同的数据操作,对象提供接收访问者的方法,从而保证数据结构的稳定性和操作的多样性。也可以理解为,封装对象的操作方法,达到不改变对象数据结构的稳定性同时易于扩展操作。

    解决的主要问题

      主要解决:稳定的数据结构和易变的操作耦合问题。

    如何实现

    (1)Visitor接口:访问者接口,封装对象元素的操作,它定义了对每一个元素(Element)访问的行为,它的参数就是可以访问的元素,它的方法数理论上来讲与元素个数是一样的。

    (2)Visitor1、Visitor2:访问者 -- 具体的访问类,它需要给出对每一个元素类访问时所产生的具体行为,如老板,会计。

    (3)Element:元素对外访问入口接口,它定义了一个接受访问者的方法(Accept),其意义是指每一个元素(子类)都要可以被访问者访问。

    (4)ElementA、ElementB:具体的元素类,它提供接受访问方法的具体实现,而这个具体的实现,通常情况下是使用访问者提供的访问该元素类的方法。

    (5)Object:定义当中所说的对象结构,对象结构是一个抽象表述,它内部管理了元素集合,并且可以迭代这些元素供访问者访问
    生活场景

    公司有一个账本,抽象为一个对象,它有两个稳定的元素,一个是收入,一个是支出。公司有不同的角色都需要访问账本,抽象为访问者。比如。老板只关注总收入和总支出,会计关心每一笔收入是否缴税,每发一笔工资是否扣税。两者间是不同操作,且还有以后可能其他角色需要访问账本。

    demo代码

    1、Visitor接口

    /**
     * 访问者接口
     * (1)查看收入账单
     * (1)查看之处账单
     */
    public interface IAccountBookViewer {
        /**
         * 消费账单
         * @param bill
         */
        void view(ConsumeBill bill);
    
        /**
         * 收入账单
         * @param bill
         */
        void view(IncomeBill bill);
    
    }
    IAccountBookViewer.java

    2、访问者

    2.1 具体类 --  老板

     1 /**
     2  * 老板角色--查看账单
     3  * 老板只关注总收入和总支出
     4  */
     5 public class Boss implements IAccountBookViewer {
     6 
     7     /**
     8      * 总消费
     9      */
    10     private Double totalConsume = 0d;//默认值是null
    11 
    12     /**
    13      * 总收入
    14      */
    15     private Double totalIncome = 0d;//默认值是null
    16 
    17     public Double getTotalConsume() {
    18         System.out.println("老板查看总支出:" + totalConsume);
    19         return totalConsume;
    20     }
    21 
    22     public Double getTotalIncome() {
    23         System.out.println("老板查看总收入:" + totalIncome);
    24         return totalIncome;
    25     }
    26 
    27 
    28     /**
    29      * 查看消费账单
    30      *
    31      * @param bill 消费账单
    32      */
    33     @Override
    34     public void view(ConsumeBill bill) {
    35         totalConsume += bill.getAmount();
    36     }
    37 
    38     /**
    39      * 查看收入账单
    40      *
    41      * @param bill 账单
    42      */
    43     @Override
    44     public void view(IncomeBill bill) {
    45         totalIncome += bill.getAmount();
    46     }
    47 }
    Boss.java

    2.2、具体类 -- 会计

     1 /**
     2  * 注册会计师
     3  * 关注具体收入和支出是否交税
     4  */
     5 public class CPA implements IAccountBookViewer {
     6     /**
     7      * 查看支出,如果是工资,是否已经交税
     8      *
     9      * @param bill
    10      */
    11     @Override
    12     public void view(ConsumeBill bill) {
    13         if (bill.getItem().equals("工资")) {
    14             System.out.println("CPA查看是否工资已经扣税");
    15         }
    16     }
    17 
    18     /**
    19      * 查看收入,所有收入都要交税
    20      *
    21      * @param bill
    22      */
    23     @Override
    24     public void view(IncomeBill bill) {
    25         System.out.println("CPA查看所有收入是否已经缴税");
    26     }
    27 }
    CPA.java

    3、元素接口,定义每个元素行为 -- 对外提供accept方法,传入访问者

    1 /**
    2  * 账单类接口,接收访问者
    3  */
    4 public interface IBill {
    5     void accept(IAccountBookViewer viewer);
    6 }
    IBill.java

    4、具体的元素类

    4.1、收入条目,可以理解为一个对象的元素

     1 /**
     2  * 收入账单
     3  */
     4 public class IncomeBill implements IBill {
     5     /**
     6      * 收入明细金额
     7      */
     8     private Double amount;
     9 
    10     /**
    11      * 收入条目
    12      */
    13     private String item;
    14 
    15     /**
    16      * 收入明细金额
    17      */
    18     public Double getAmount() {
    19         return amount;
    20     }
    21 
    22     /**
    23      * 收入条目
    24      */
    25     public String getItem() {
    26         return item;
    27     }
    28 
    29 
    30     public IncomeBill(Double amount, String item) {
    31         this.amount = amount;
    32         this.item = item;
    33     }
    34 
    35     @Override
    36     public void accept(IAccountBookViewer viewer) {
    37         viewer.view(this);
    38     }
    39 }
    IncomeBill.java

    4.2、支出条目,可以理解为一个对象元素

     1 /**
     2  * 消费账单
     3  */
     4 public class ConsumeBill implements IBill {
     5 
     6     /**
     7      * 支出明细金额
     8      */
     9     private Double amount;
    10 
    11     /**
    12      * 支出条目
    13      */
    14     private String item;
    15 
    16     /**
    17      * 支出明细金额
    18      */
    19     public Double getAmount() {
    20         return amount;
    21     }
    22 
    23     /**
    24      * 支出条目
    25      */
    26     public String getItem() {
    27         return item;
    28     }
    29 
    30 
    31     public ConsumeBill(Double amount, String item) {
    32         this.amount = amount;
    33         this.item = item;
    34     }
    35 
    36     @Override
    37     public void accept(IAccountBookViewer viewer) {
    38         viewer.view(this);
    39     }
    40 }
    ConsumeBill.java

    5、对象类

    包含具体元素的集合,提供访问集合元素的轮询方法show

     1 import java.util.ArrayList;
     2 import java.util.List;
     3 
     4 /**
     5  * 账本类
     6  */
     7 public class AccountBook {
     8     /**
     9      * 账单条目列表
    10      */
    11     private List<IBill> billList = new ArrayList<IBill>();
    12 
    13     /**
    14      * 增加账单条目
    15      * @param bill 账单条目
    16      */
    17     public void addBill(IBill bill){
    18         billList.add(bill);
    19     }
    20 
    21     /**
    22      * 访问者产看账本
    23      * @param viewer
    24      */
    25     public void show(IAccountBookViewer viewer){
    26         for (IBill bill : billList) {
    27             bill.accept(viewer);
    28         }
    29     }
    30 
    31 }
    AccountBook.java

    测试入口

     1 public class App {
     2     public static void     (String[] args) {
     3         //创建账本
     4         AccountBook accBook = new AccountBook();
     5 
     6         //收入条目
     7         accBook.addBill(new IncomeBill(10000d, "广告收入"));
     8         accBook.addBill(new IncomeBill(900000d, "房地产项目"));
     9 
    10         //支出条目
    11         accBook.addBill(new ConsumeBill(20000d, "工资"));
    12         accBook.addBill(new ConsumeBill(10000d, "办公室租金"));
    13         accBook.addBill(new ConsumeBill(8000d, "水电费"));
    14 
    15         //访问者
    16         Boss boss = new Boss();
    17         CPA cpa = new CPA();
    18 
    19         //访问者查看账单
    20         accBook.show(boss);
    21         accBook.show(cpa);
    22 
    23         boss.getTotalConsume();
    24         boss.getTotalIncome();
    25     }
    26 }
    27     
    main方法

    输出结果

    1 CPA查看所有收入是否已经缴税
    2 CPA查看所有收入是否已经缴税
    3 CPA查看是否工资已经扣税
    4 老板查看总支出:38000.0
    5 老板查看总收入:910000.0

    示例源码:https://github.com/LF20160912/pattern

    如果该文章对你有所帮助,请点个赞支持下,谢谢!
  • 相关阅读:
    使用json序列化类型为“ajax学习.DataSetComment+T_CommentDataTable”的对象时检测到循环引用。
    CKEditor在asp.net上使用的图例详解
    去掉 win7 “测试模式 windows7 内部版本7601” 字样
    Java中非静态方法是否共用同一块内存?
    最长公共子串(LCS)
    [链表]复杂链表的复制
    最长公共子序列
    最大子序列和问题
    [ 队列]从上往下遍历二元树
    [链表]在O(1)时间删除链表结点
  • 原文地址:https://www.cnblogs.com/lfhappy/p/10980157.html
Copyright © 2020-2023  润新知