1 測評系統(tǒng)需求
某餐廳推出了一款飲料位谋,想讓品嘗著對其評價成功或失敗。品嘗著可以是男人遥椿、女人基矮,評價可以是成功或失敗。傳統(tǒng)方案是寫一個Person類冠场,然后讓Man和Woman類繼承Person并實現(xiàn)評價功能家浇。傳統(tǒng)方案擴展性不好,如果要增加新的人員類型或評價結(jié)果碴裙,需要修改Person及其子類钢悲,違反開閉原則。而訪問者模式可以很好地解決這個問題舔株。
2 訪問者模式介紹
訪問者模式封裝了一些作用于某種數(shù)據(jù)結(jié)構(gòu)的各元素的操作莺琳,它可以在不改變數(shù)據(jù)結(jié)構(gòu)的前提下定義作用于這些元素的新的操作,這種設計模式將數(shù)據(jù)結(jié)構(gòu)與數(shù)據(jù)操作分離载慈,解決數(shù)據(jù)結(jié)構(gòu)和操作耦合性問題惭等。訪問者模式的基本工作原理是:在被訪問的類里面加一個對外提供接待訪問者的接口。
角色分析:
1)Visitor:抽象訪問者办铡,為該對象結(jié)構(gòu)中的ConcreteElement的每一個類聲明一個visit操作辞做。
2)ConcreteVisitor:具體的訪問者琳要,實現(xiàn)Visitor聲明的操作,是每個操作到的具體實現(xiàn)秤茅。
3)ObjectStructure:對象結(jié)構(gòu)稚补,它能枚舉具體的元素,可以提供一個高層的接口嫂伞,來允許訪問者訪問元素孔厉。
4)Element:元素的抽象類,定義一個accept方法帖努,接收一個訪問者對象撰豺。
5)ConcreteElement:元素的具體實現(xiàn),實現(xiàn)了accept方法拼余。
3 用訪問者模式實現(xiàn)評分需求
首先有抽象的訪問者Action:
public abstract class Action {
abstract void getManResult(Person man);
abstract void getWomanResult(Person woman);
}
訪問者的具體實現(xiàn)Success污桦、Fail:
public class Success extends Action {
@Override
void getManResult(Person man) {
System.out.println("男人給的評價是:成功");
}
@Override
void getWomanResult(Person woman) {
System.out.println("女人給的評價是:成功");
}
}
public class Fail extends Action {
@Override
void getManResult(Person man) {
System.out.println("男人給的評價是:失敗");
}
@Override
void getWomanResult(Person woman) {
System.out.println("女人給的評價是:失敗");
}
}
元素的抽象類Person:
public abstract class Person {
abstract void accept(Action action);
}
具體的元素實現(xiàn)類Man、Woman匙监,需要注意的是:這里使用了雙分派的方式凡橱,首先在客戶端程序中,將具體的狀態(tài)作為參數(shù)傳遞到Man或Woman中(第一次分派)亭姥,然后在Man或Woman類中調(diào)用了作為參數(shù)的"具體方法"中的getWomanResult稼钩,同時將自己(即this)作為參數(shù)傳入(第二次分派)。
public class Man extends Person {
@Override
void accept(Action action) {
action.getManResult(this);
}
}
public class Woman extends Person {
@Override
void accept(Action action) {
action.getWomanResult(this);
}
}
ObjectStructure類:
public class ObjectStructure {
// 維護一個集合
private List<Person> persons = new LinkedList<>();
public void attach(Person person) {
persons.add(person);
}
public void detach(Person person) {
persons.remove(person);
}
// 顯示測評結(jié)果
public void display(Action action) {
for (Person person : persons) {
person.accept(action);
}
}
}
客戶端調(diào)用:
public class Client {
public static void main(String[] args) {
// 創(chuàng)建ObjectStructure對象
ObjectStructure objectStructure = new ObjectStructure();
objectStructure.attach(new Man());
objectStructure.attach(new Woman());
objectStructure.attach(new Man());
// 成功的評價
Success success = new Success();
objectStructure.display(success);
System.out.println("====================");
// 失敗的評價
Fail fail = new Fail();
objectStructure.display(fail);
}
}
輸出結(jié)果:
男人給的評價是:成功
女人給的評價是:成功
男人給的評價是:成功
====================
男人給的評價是:失敗
女人給的評價是:失敗
男人給的評價是:失敗
4 訪問者模式的優(yōu)缺點
優(yōu)點:
1)訪問者模式符合單一職責原則达罗,可以讓程序具有優(yōu)秀的擴展性和靈活性坝撑。
2)訪問者模式可以對功能進行統(tǒng)一,可以做報表粮揉、UI巡李、攔截器與過濾器,適用于數(shù)據(jù)結(jié)構(gòu)穩(wěn)定地系統(tǒng)扶认。
缺點:
1)對具體元素訪問者公布細節(jié)侨拦,也就是說訪問者關注了其他類的內(nèi)部細節(jié),這是迪米特法則所不建議的辐宾,這樣會造成具體元素的變更比較困難狱从。
2)違背了依賴倒轉(zhuǎn)原則,訪問者依賴的是具體元素叠纹,而不是抽象類矫夯。
因此,如果一個系統(tǒng)具有較穩(wěn)定地數(shù)據(jù)結(jié)構(gòu)吊洼,又有經(jīng)常變化的功能需求训貌,那么訪問者模式是比較合適的。