访问者模式定义:
封装一些作用于某种数据结构中的各元素的操作,它可以在不改变这个数据结构的前提下定义作用于这些元素的新的操作。
理解:
假设有一种数据结构,有操作想访问这中数据结构,但是又不想破坏数据结构,所以就要定义访问者来访问。
以账单为例:
公司看账单的有财务和老板,账单有支出和收入账单,账单的数据结构肯定是不能变化的,一旦生成了账单,就只能看,不能改。
但是访问者是多变的,比如财务看账单的时候,会汇总,计算税务,老板看账单的时候,会看看明细。
只要访问者改变,那么看账单的行为实现是不同的,但是账单永远是不变的。
代码实现:
账单接口:
public interface Bill {
void view(Viewer viewer);
}
收入账单和消费账单:
public class IncomeBill implements Bill {
private BigDecimal amount;
private String item;
public IncomeBill(BigDecimal amount, String item) {
this.amount = amount;
this.item = item;
}
@Override
public void view(Viewer viewer) {
viewer.view(this);
}
public BigDecimal getAmount() {
return amount;
}
public void setAmount(BigDecimal amount) {
this.amount = amount;
}
public String getItem() {
return item;
}
public void setItem(String item) {
this.item = item;
}
}
public class ComsumeBill implements Bill {
private BigDecimal amount;
private String item;
public ComsumeBill(BigDecimal amount, String item) {
this.amount = amount;
this.item = item;
}
@Override
public void view(Viewer viewer) {
viewer.view(this);
}
public BigDecimal getAmount() {
return amount;
}
public void setAmount(BigDecimal amount) {
this.amount = amount;
}
public String getItem() {
return item;
}
public void setItem(String item) {
this.item = item;
}
}
访问者:
public interface Viewer {
void view(IncomeBill incomeBill);
void view(ComsumeBill comsumeBill);
}
老板访问者和财务访问者:
public class Boss implements Viewer {
private BigDecimal totalIncomeAmount = new BigDecimal("0");
private BigDecimal totalComsumeAmount = new BigDecimal("0");
@Override
public void view(IncomeBill incomeBill) {
totalIncomeAmount = totalIncomeAmount.add(incomeBill.getAmount());
}
@Override
public void view(ComsumeBill comsumeBill) {
totalComsumeAmount = totalComsumeAmount.add(comsumeBill.getAmount());
}
public BigDecimal getTotalIncomeAmount() {
return totalIncomeAmount;
}
public BigDecimal getTotalComsumeAmount() {
return totalComsumeAmount;
}
public void showTotalIncome(){
System.out.println("收入总额是:"+ totalIncomeAmount);
}
public void showTotalComsume(){
System.out.println("支出总额是:" + totalComsumeAmount);
}
}
账本类当中有一个列表,这个列表是元素(Bill)的集合,这便是对象结构的通常表示,它一般会是一堆元素的集合,不过这个集合不一定是列表,也可能是树,链表等等任何数据结构,甚至是若干个数据结构。其中show方法,就是账本类的精髓,它会枚举每一个元素,让访问者访问。
public class Finance implements Viewer {
private BigDecimal totalIncomeTax = new BigDecimal("0");
private BigDecimal totalComsumeTax = new BigDecimal("0");
@Override
public void view(IncomeBill incomeBill) {
totalIncomeTax = totalIncomeTax.add(incomeBill.getAmount().multiply(new BigDecimal("0.02")));
}
@Override
public void view(ComsumeBill comsumeBill) {
totalComsumeTax = totalComsumeTax.add(comsumeBill.getAmount().multiply(new BigDecimal("0.03")));
}
public BigDecimal getTotalIncomeTax() {
return totalIncomeTax;
}
public BigDecimal getTotalComsumeTax() {
return totalComsumeTax;
}
public void showTotalIncomeTax(){
System.out.println("收入总税是:"+ totalIncomeTax);
}
public void showTotalComsumeTax(){
System.out.println("支出总税是:" + totalComsumeTax);
}
}
测试代码:
public class VisitorTest {
public static void main(String[] args) {
AccountBook accountBook = new AccountBook();
accountBook.addBill(new IncomeBill(new BigDecimal("112.3"),"卖材料"));
accountBook.addBill(new IncomeBill(new BigDecimal("105.3"),"平台服务赚取"));
accountBook.addBill(new ComsumeBill(new BigDecimal("50.3"),"买办公器材"));
accountBook.addBill(new ComsumeBill(new BigDecimal("15.3"),"内部培训"));
Boss boss = new Boss();
Finance finance = new Finance();
accountBook.show(boss);
accountBook.show(finance);
boss.showTotalComsume();
boss.showTotalIncome();
finance.showTotalComsumeTax();
finance.showTotalIncomeTax();
}
}
测试结果:
支出总额是:65.6
收入总额是:217.6
支出总税是:1.968
收入总税是:4.352
总结
优点:
1、使得数据结构和作用于结构上的操作解耦,使得操作集合可以独立变化。
2、添加新的操作或者说访问者会非常容易。
3、将对各个元素的一组操作集中在一个访问者类当中。
4、使得类层次结构不改变的情况下,可以针对各个层次做出不同的操作,而不影响类层次结构的完整性。
5、可以跨越类层次结构,访问不同层次的元素类,做出相应的操作。
缺点:
1、增加新的元素会非常困难。
2、实现起来比较复杂,会增加系统的复杂性。
3、破坏封装,如果将访问行为放在各个元素中,则可以不暴露元素的内部结构和状态,但使用访问者模式的时候,为了让访问者能获取到所关心的信息,元素类不得不暴露出一些内部的状态和结构,就像收入和支出类必须提供访问金额和单子的项目的方法一样。
适用性:
1、数据结构稳定,作用于数据结构的操作经常变化的时候。
2、当一个数据结构中,一些元素类需要负责与其不相关的操作的时候,为了将这些操作分离出去,以减少这些元素类的职责时,可以使用访问者模式。
3、有时在对数据结构上的元素进行操作的时候,需要区分具体的类型,这时使用访问者模式可以针对不同的类型,在访问者类中定义不同的操作,从而去除掉类型判断。