1. 基本概念
在訪問者模式(Visitor Pattern)中细诸,我們使用了一個(gè)訪問者類肚菠,它改變了元素類的執(zhí)行算法褒链。通過這種方式苔货,元素的執(zhí)行算法可以隨著訪問者改變而改變。這種類型的設(shè)計(jì)模式屬于行為型模式沪蓬。根據(jù)模式,元素對(duì)象已接受訪問者對(duì)象,這樣訪問者對(duì)象就可以處理元素對(duì)象上的操作幻件。
2. 基本實(shí)現(xiàn)思路
(1)Visitor:接口或者抽象類,它定義了對(duì)每一個(gè)元素(Element)訪問的行為蛔溃,它的參數(shù)就是可以訪問的元素绰沥,它的方法數(shù)理論上來講與元素個(gè)數(shù)是一樣的,因此贺待,訪問者模式要求元素的類族要穩(wěn)定徽曲,如果經(jīng)常添加、移除元素類麸塞,必然會(huì)導(dǎo)致頻繁地修改Visitor接口秃臣,如果這樣則不適合使用訪問者模式。
(2)ConcreteVisitor1喘垂、ConcreteVisitor2:具體的訪問類甜刻,它需要給出對(duì)每一個(gè)元素類訪問時(shí)所產(chǎn)生的具體行為。
(3)Element:元素接口或者抽象類正勒,它定義了一個(gè)接受訪問者的方法(Accept)得院,其意義是指每一個(gè)元素都要可以被訪問者訪問。
(4)ConcreteElementA章贞、ConcreteElementB:具體的元素類祥绞,它提供接受訪問方法的具體實(shí)現(xiàn),而這個(gè)具體的實(shí)現(xiàn)鸭限,通常情況下是使用訪問者提供的訪問該元素類的方法蜕径。
(5)ObjectStructure:定義當(dāng)中所說的對(duì)象結(jié)構(gòu),對(duì)象結(jié)構(gòu)是一個(gè)抽象表述败京,它內(nèi)部管理了元素集合兜喻,并且可以迭代這些元素供訪問者訪問。
3. 代碼示例
元素類
//單個(gè)單子的接口
public interface Bill {
void accept(FinanceOrderViewer viewer);
}
具體的元素類
// 消費(fèi)的單子
class ConsumeBill implements Bill {
private double amount;
private String item;
public ConsumeBill(double amount, String item) {
super();
this.amount = amount;
this.item = item;
}
@Override
public void accept(FinanceOrderViewer 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;
}
@Override
public void accept(FinanceOrderViewer viewer) {
viewer.view(this);
}
public double getAmount() {
return amount;
}
public String getItem() {
return item;
}
}
對(duì)象結(jié)構(gòu)
// 賬本類
public class FinanceOrder {
// 單子列表
private List<Bill> billList = new ArrayList<>();
// 添加單子
public void addBill(Bill bill) {
billList.add(bill);
}
// 供賬本的查看者查看賬本
public void show(FinanceOrderViewer viewer) {
for (Bill bill : billList) {
bill.accept(viewer);
}
}
}
訪問者接口
// 賬單查看者接口(相當(dāng)于Visitor)
public interface FinanceOrderViewer {
// 查看消費(fèi)的單子
void view(ConsumeBill bill);
// 查看收入的單子
void view(IncomeBill bill);
}
具體訪問者
//會(huì)計(jì)類
public class Accountant implements FinanceOrderViewer {
@Override
public void view(ConsumeBill bill) {
if (bill.getItem().equals("出口稅費(fèi)")) {
System.out.println("會(huì)計(jì)查看出口稅費(fèi)赡麦,可能有退稅政策:" + bill.getAmount());
} else {
System.out.println("會(huì)計(jì)查看本次花費(fèi):" + bill.getAmount());
}
}
// 如果是收入朴皆,則所有的收入都要交稅
@Override
public void view(IncomeBill bill) {
System.out.println("會(huì)計(jì)查看本次收入:" + bill.getAmount());
}
}
//經(jīng)理類,查看賬本的類之一
class Manager implements FinanceOrderViewer {
@Override
public void view(ConsumeBill bill) {
System.out.println("經(jīng)理查看本次支出:"+ bill.getAmount());
}
@Override
public void view(IncomeBill bill) {
System.out.println("經(jīng)理查看本次收入:" + bill.getAmount());
}
}
測(cè)試
class Test {
public static void main(String[] args) {
FinanceOrder accountBook = new FinanceOrder();
// 添加兩條收入
accountBook.addBill(new IncomeBill(10000,"出口貿(mào)易"));
accountBook.addBill(new IncomeBill(12000,"租賃庫房"));
// 添加兩條支出
accountBook.addBill(new ConsumeBill(1000,"出口稅費(fèi)"));
accountBook.addBill(new ConsumeBill(2000,"材料費(fèi)"));
FinanceOrderViewer manager = new Manager();
FinanceOrderViewer accountant = new Accountant();
// 兩個(gè)訪問者分別訪問賬本
accountBook.show(manager);
accountBook.show(accountant);
}
}
輸出結(jié)果
經(jīng)理查看本次收入:10000.0
經(jīng)理查看本次收入:12000.0
經(jīng)理查看本次支出:1000.0
經(jīng)理查看本次支出:2000.0
會(huì)計(jì)查看本次收入:10000.0
會(huì)計(jì)查看本次收入:12000.0
會(huì)計(jì)查看出口稅費(fèi)泛粹,可能有退稅政策:1000.0
會(huì)計(jì)查看本次花費(fèi):2000.0
4. 總結(jié)
優(yōu)點(diǎn):
- 穩(wěn)定的數(shù)據(jù)結(jié)構(gòu)遂铡,與可變的操作結(jié)構(gòu)之間的解耦
- 增加訪問者比較方便
缺點(diǎn):
- 需要暴露元素對(duì)象類相關(guān)的狀態(tài)及結(jié)構(gòu)
- 增加元素類數(shù)據(jù)結(jié)構(gòu)時(shí),將會(huì)非常困難