15.访问者模式
在被访问的类里添加一个接待外部访问者的接口,在对一个对象结构中的对象做多个复杂操作的时候,又 不想让操作污染这些对象,可以使用该模式
UML类图介绍:
Bill接口,这个接口有一个accept方法去接受访问者
/** * @author wuyimin * @create 2021-07-28 15:57 * @description 账单的接口 */ public interface Bill { public void accept(Visitor visitor); }
Visitor接口。这个Bill接口有两个方法接受Bill接口的两个实现类对象
/** * @author wuyimin * @create 2021-07-28 15:57 * @description 访问者 */ public interface Visitor { //查看收入 public void visitInput(InputBill inputBill); //查看支出 public void visitOutput(OutputBill outputBill); }
Bill接口的两个实现类,其重点主要在于重写accpet方法分别去接受visitor对应自己的方法
/** * @author wuyimin * @create 2021-07-28 16:02 * @description 收入的账单 */ public class InputBill implements Bill{ //项目 private String item; //收入的钱数目 private double money; public String getItem() { return item; } public void setItem(String item) { this.item = item; } public double getMoney() { return money; } public void setMoney(double money) { this.money = money; } public InputBill(String item, double money) { this.item = item; this.money = money; } @Override public void accept(Visitor visitor) { visitor.visitInput(this); } }
/** * @author wuyimin * @create 2021-07-28 15:59 * @description 支出的账单 */ public class OutputBill implements Bill { //项目 private String item; //支出的钱数目 private double money; public String getItem() { return item; } public void setItem(String item) { this.item = item; } public double getMoney() { return money; } public void setMoney(double money) { this.money = money; } public OutputBill(String item, double money) { this.item = item; this.money = money; } @Override public void accept(Visitor visitor) { visitor.visitOutput(this); } }
两个Visitor,分别是Boss和CPA
/** * @author wuyimin * @create 2021-07-28 16:04 * @description 老板类,老板只关系总收入和总支出,所以在方法里只是统计就行了 */ public class Boss implements Visitor { private double totalIncome; private double totalOutcome; @Override public void visitInput(InputBill inputBill) { totalIncome+=inputBill.getMoney(); } @Override public void visitOutput(OutputBill outputBill) { totalOutcome+=outputBill.getMoney(); } public double getTotalIncome(){ System.out.println("老板查看总收入为:"+totalIncome); return totalIncome; } public double getTotalOutcome(){ System.out.println("老板查看总支出为:"+totalOutcome); return totalOutcome; } }
/** * @author wuyimin * @create 2021-07-28 16:10 * @description 注册会计师类,每次添加一个项目都会检查 */ public class CPA implements Visitor { @Override public void visitInput(InputBill inputBill) { System.out.println("查看收入是否交税了"); } @Override public void visitOutput(OutputBill outputBill) { if(outputBill.getItem().equals("工资")){ System.out.println("查看工资是否交税了"); } } }
对象结构类,也就是账本类,在show方法中调用bill的accept接口
/** * @author wuyimin * @create 2021-07-28 16:13 * @description 账本类,对象结构 */ public class AccountBook { public ArrayList<Bill> billList=new ArrayList<>(); public void add(Bill bill){ billList.add(bill); } //提供接口给账本的查看者查看账本 public void show(Visitor visitor){ for (Bill bill:billList) { bill.accept(visitor); } } //测试的客户端 public static void main(String[] args) { AccountBook accountBook = new AccountBook(); //添加两条收入 accountBook.add(new InputBill("卖商品",10000)); accountBook.add(new InputBill( "卖广告位",12000)); //添加两条支出 accountBook.add(new OutputBill( "工资",1000)); accountBook.add(new OutputBill("材料费",2000)); Boss boss = new Boss(); CPA cpa = new CPA(); //两个访问者分别访问账本 accountBook.show(cpa); accountBook.show(boss); //老板查账 boss.getTotalOutcome(); boss.getTotalIncome(); } }
上面的代码中,可以这么理解,账本以及账本中的元素是非常稳定的,这些几乎不可能改变,而最容易改变的就是访问者这部分。
访问者模式最大的优点就是增加访问者非常容易,我们从代码上来看,如果要增加一个访问者,你只需要做一件事即可,那就是写一个类,实现AccountBookViewer接口,然后就可以直接调用AccountBook的show方法去访问账本了。
如果没使用访问者模式,一定会增加许多if else,而且每增加一个访问者,你都需要改你的if else,代码会显得非常臃肿,而且非常难以扩展和维护。
如果将上述代码的visitInput和visitOutput重载为两个Visit方法的话,我们就可以说访问者模式是伪动态双分派的
优点:
1、使得数据结构和作用于结构上的操作解耦,使得操作集合可以独立变化。
2、添加新的操作或者说访问者会非常容易。
3、将对各个元素的一组操作集中在一个访问者类当中。
4、使得类层次结构不改变的情况下,可以针对各个层次做出不同的操作,而不影响类层次结构的完整性。
5、可以跨越类层次结构,访问不同层次的元素类,做出相应的操作。
缺点:
1、增加新的元素会非常困难。
2、实现起来比较复杂,会增加系统的复杂性。
3、破坏封装,如果将访问行为放在各个元素中,则可以不暴露元素的内部结构和状态,但使用访问者模式的时候,为了让访问者能获取到所关心的信息,元素类不得不暴露出一些内部的状态和结构,就像收入和支出类必须提供访问金额和单子的项目的方法一样。