设计模式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

posted @ 2019-06-05 16:06  天晴修屋顶  阅读(151)  评论(0编辑  收藏  举报