1计贰、什么是訪問者模式钦睡?
? 訪問者設(shè)計模式的目的是表示要對對象結(jié)構(gòu)的元素執(zhí)行的操作。Visitor允許您定義一個新的操作躁倒,而不需要更改它所操作的元素的類荞怒。Visitor模式允許在不更改集合中任何對象的類的情況下定義操作。要實現(xiàn)這一點秧秉,Visitor模式建議在一個稱為Visitor類的單獨類中定義操作褐桌。這將操作從它所操作的對象集合中分離出來。對于每個要定義的新操作象迎,都會創(chuàng)建一個新的訪問者類荧嵌。
2、場景分析
我們都知道財務(wù)都是有賬本的砾淌,這個賬本就可以作為一個對象結(jié)構(gòu)啦撮,而它其中的元素有兩種,收入和支出,這滿足我們訪問者模式的要求,即元素的個數(shù)是穩(wěn)定的牵素,因為賬本中的元素只能是收入和支出。
而查看賬本的人可能有這樣幾種织中,比如老板锥涕,會計事務(wù)所的注會,財務(wù)主管狭吼,等等层坠。而這些人在看賬本的時候顯然目的和行為是不同的。
首先我們給出單子的接口刁笙,它只有一個方法accept破花。
public interface Bill {
void accept(AccountBookViewer viewer);
}
其中的方法參數(shù)AccountBookViewer是一個賬本訪問者接口,接下來也就是實現(xiàn)類采盒,收入單子和消費單子旧乞,或者說收入和支出類。
//消費的單子
public class ConsumeBill implements Bill {
private double amount;
private String item;
public ConsumeBill(double amount, String item) {
super();
this.amount = amount;
this.item = item;
}
public void accept(AccountBookViewer viewer) {
viewer.view(this);
}
public double getAmount() {
return amount;
}
public String getItem() {
return item;
}
}
//收入單子
public class IncomeBill implements Bill {
private double amount;
private String item;
public IncomeBill(double amount, String item) {
super();
this.amount = amount;
this.item = item;
}
public void accept(AccountBookViewer viewer) {
viewer.view(this);
}
public double getAmount() {
return amount;
}
public String getItem() {
return item;
}
}
上面最關(guān)鍵的還是里面的accept方法磅氨,它直接讓訪問者訪問自己,這相當于一次靜態(tài)分派(文章最后進行解釋)嫡纠,當然我們也可以不使用重載而直接給方法不同的名稱烦租。
接下來是賬本訪問者接口
//賬單查看者接口(相當于Visitor)
public interface AccountBookViewer {
//查看消費的單子
void view(ConsumeBill bill);
//查看收入的單子
void view(IncomeBill bill);
}
這兩個方法是重載方法,就是在上面的元素類當中用到的除盏,當然你也可以按照訪問者模式類圖當中的方式去做叉橱,將兩個方法分別命名為viewConsumeBill和viewIncomeBill,而一般建議按照類圖上來做的
訪問者的實現(xiàn)
//老板類者蠕,查看賬本的類之一
public class Boss implements AccountBookViewer {
private double totalIncome;
private double totalConsume;
//老板只關(guān)注一共花了多少錢以及一共收入多少錢窃祝,其余并不關(guān)心
public void view(ConsumeBill bill) {
totalConsume += bill.getAmount();
}
public void view(IncomeBill bill) {
totalIncome += bill.getAmount();
}
public double getTotalIncome() {
System.out.println("老板查看一共收入多少,數(shù)目是:" + totalIncome);
return totalIncome;
}
public double getTotalConsume() {
System.out.println("老板查看一共花費多少踱侣,數(shù)目是:" + totalConsume);
return totalConsume;
}
}
//注冊會計師類粪小,查看賬本的類之一
public class CPA implements AccountBookViewer {
//注會在看賬本時,如果是支出抡句,則如果支出是工資探膊,則需要看應(yīng)該交的稅交了沒
public void view(ConsumeBill bill) {
if (bill.getItem().equals("工資")) {
System.out.println("注會查看工資是否交個人所得稅。");
}
}
//如果是收入待榔,則所有的收入都要交稅
public void view(IncomeBill bill) {
System.out.println("注會查看收入交稅了沒逞壁。");
}
}
老板只關(guān)心收入和支出的總額,而注會只關(guān)注該交稅的是否交稅
接下來是賬本類锐锣,它是當前訪問者模式例子中的對象結(jié)構(gòu)
//賬本類(相當于ObjectStruture)
public 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);
}
}
}
賬本類當中有一個列表腌闯,這個列表是元素(Bill)的集合,這便是對象結(jié)構(gòu)的通常表示雕憔,它一般會是一堆元素的集合姿骏,不過這個集合不一定是列表,也可能是樹橘茉,鏈表等等任何數(shù)據(jù)結(jié)構(gòu)工腋,甚至是若干個數(shù)據(jù)結(jié)構(gòu)姨丈。其中show方法,就是賬本類的精髓擅腰,它會枚舉每一個元素蟋恬,讓訪問者訪問。
public class Client {
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();
}
}
上面的代碼中趁冈,可以這么理解歼争,賬本以及賬本中的元素是非常穩(wěn)定的,這些幾乎不可能改變渗勘,而最容易改變的就是訪問者這部分沐绒。