Java设计模式之行为型:访问者模式
背景:
? ? ? ? 去醫(yī)院看病時,醫(yī)生會給你一個處方單要你去拿藥,拿藥我們可以分為兩步走:
- (1)去柜臺交錢,劃價(jià)人員會根據(jù)處方單上的藥進(jìn)行劃價(jià),交錢。
- (2)去藥房拿藥,藥房工作者同樣根據(jù)處方單給你相對應(yīng)的藥。
????????這里我們就劃價(jià)和拿藥兩個步驟進(jìn)行討論,這里有三個類,處方單(藥)、劃價(jià)人員、藥房工作者。同時劃價(jià)人員和藥房工作者都各自有一個動作:劃價(jià)、拿藥。這里進(jìn)行最初步的設(shè)計(jì)如下:
劃價(jià)人員:
public class Charge {public void action(){public void action(){if("A藥".equals(medicine)){//A的價(jià)格}if("B藥".equals(medicine)){//B的價(jià)格}if("C藥".equals(medicine)){//C的價(jià)格}if("D藥".equals(medicine)){//D的價(jià)格}if("E藥".equals(medicine)){//E的價(jià)格}............}} }藥房工作者:
public class WorkerOfPharmacy {public void action(){if("A藥".equals(medicine)){//給你A藥}if("B藥".equals(medicine)){//給你B藥}if("C藥".equals(medicine)){//給你C藥}if("D藥".equals(medicine)){//給你D藥}if("E藥".equals(medicine)){//給你E藥}............} }? ? ? ? 這樣的代碼寫法,在藥品種類少的情況沒什么問題,但也存在這么多的 if…else,而且我們可以想象醫(yī)院里的藥是那么多,而且隨時都會增加的,增加了藥就要改變劃價(jià)人員和藥房工作者的代碼,這是我們最不希望改變的。那么有沒有辦法來解決呢?有,訪問者模式提供一中比較好的解決方案。
????????在實(shí)際開發(fā)過程中,我們對同個對象可能存在不同的操作方式,如處方單,劃價(jià)人員要根據(jù)它來劃價(jià),藥房工作者要根據(jù)它來給藥。而且可能會隨時增加新的操作,如醫(yī)院增加新的藥物,但是這里有兩個元素是保持不變的,或者說很少變:劃價(jià)人員和藥房工作中,變的只不過是他們的操作。所以我們想如果能夠?qū)⑺麄兊牟僮鞒橄蠡秃昧?#xff0c;這里訪問者模式就是一個值得考慮的解決方案了。
一、什么是訪問者模式:
? ? ? ? 訪問者模式適用于數(shù)據(jù)結(jié)構(gòu)相對穩(wěn)定的系統(tǒng),將數(shù)據(jù)結(jié)構(gòu)與基于數(shù)據(jù)的操作進(jìn)行分離,使得添加作用于這些數(shù)據(jù)結(jié)構(gòu)的新操作變得簡單,并且不需要改變各數(shù)據(jù)結(jié)構(gòu),為不同類型的數(shù)據(jù)結(jié)構(gòu)提供多種訪問操作方式,這樣是訪問者模式的設(shè)計(jì)動機(jī)。
? ? ? ? 除了使新增訪問操作變得更加簡單,也能夠在不修改現(xiàn)有類的層次結(jié)構(gòu)下,定義該類層次結(jié)構(gòu)的操作,并將有關(guān)元素對象的訪問行為集中到一個訪問者對象中,而不是分散搞一個個的元素類中。
? ? ? ?但訪問者模式的缺點(diǎn)在于讓增加新的元素類變得困難,每增加一個新的元素類都意味著要在抽象訪問者角色中增加一個新的抽象操作,并在每一個具體訪問者類中增加相應(yīng)的具體操作,違背了“開閉原則”的要求;
? ? ? ? 所以訪問者模式適用于對象結(jié)構(gòu)中很少改變,但經(jīng)常需要在此對象結(jié)構(gòu)上定義新的操作的系統(tǒng),使得算法操作的增加變得簡單;或者需要對一個對象結(jié)構(gòu)中進(jìn)行很多不同并且不相關(guān)的操作,并且需要避免讓這些操作污染這些對象,也不希望在增加新操作時修改這些類的場景
二、UML結(jié)構(gòu)圖:
- Vistor:抽象訪問者,聲明了對?ConcreteElement 類的一些操作?
- ConcreteVisitor:具體訪問者,實(shí)現(xiàn)抽象訪問者中聲明的每一個操作
- Element:抽象元素,定義一個 accept 操作,用于接收具體訪問者?
- ConcreteElement:具體元素 ,實(shí)現(xiàn) accept 操作。?
- ObjectStructure:對象結(jié)構(gòu),提供一個高層接口來允許訪問者枚舉它的元素
????????從上面的 UML結(jié)構(gòu)圖中我們可以看出,訪問者模式主要分為兩個層次結(jié)構(gòu),一個是訪問者層次結(jié)構(gòu),提供了抽象訪問者和具體訪問者,主要用于聲明一些操作;一個是元素層次結(jié)構(gòu),提供了抽象元素和具體元素,主要用于聲明 accept 操作;而對象結(jié)構(gòu)作為兩者的橋梁,存儲了不同類型的對象,以便不同的訪問者來訪問,相同訪問者可以以不同的方式訪問不同的元素,所以在訪問者模式中增加新的訪問者無需修改現(xiàn)有代碼,可擴(kuò)展行強(qiáng)。
????????在訪問者模式使用了雙分派技術(shù),所謂雙分派技術(shù)就是在選擇方法的時候,不僅僅要根據(jù)消息接收者的運(yùn)行時區(qū)別,還要根據(jù)參數(shù)的運(yùn)行時區(qū)別。在訪問者模式中,客戶端將具體狀態(tài)當(dāng)做參數(shù)傳遞給具體訪問者,這里完成第一次分派,然后具體訪問者作為參數(shù)的“具體狀態(tài)”中的方法,同時也將自己this作為參數(shù)傳遞進(jìn)去,這里就完成了第二次分派。雙分派意味著得到的執(zhí)行操作決定于請求的種類和接受者的類型。
二、模式的實(shí)現(xiàn):
????????以上面在醫(yī)院付費(fèi)、取藥為實(shí)例。在這個實(shí)例中劃價(jià)員和藥房工作者作為訪問者,藥品作為訪問元素、處方單作為對象結(jié)構(gòu),所以整個UML結(jié)構(gòu)圖如下:
抽象訪問者:Visitor.java
public abstract class Visitor {protected String name;public void setName(String name) {this.name = name;}public abstract void visitor(MedicineA a);public abstract void visitor(MedicineB b); }具體訪問者:劃價(jià)員、Charger.java
public class Charger extends Visitor{public void visitor(MedicineA a) {System.out.println("劃價(jià)員:" + name +"給藥" + a.getName() +"劃價(jià):" + a.getPrice());}public void visitor(MedicineB b) {System.out.println("劃價(jià)員:" + name +"給藥" + b.getName() +"劃價(jià):" + b.getPrice());} }具體訪問者:藥房工作者、WorkerOfPharmacy.java
public class WorkerOfPharmacy extends Visitor{public void visitor(MedicineA a) {System.out.println("藥房工作者:" + name + "拿藥 :" + a.getName());}public void visitor(MedicineB b) {System.out.println("藥房工作者:" + name + "拿藥 :" + b.getName());} }抽象元素:Medicine.java
public abstract class Medicine {protected String name;protected double price;public Medicine (String name,double price){this.name = name;this.price = price;}public String getName() {return name;}public void setName(String name) {this.name = name;}public double getPrice() {return price;}public void setPrice(double price) {this.price = price;}public abstract void accept(Visitor visitor); }具體元素:MedicineA.java
public class MedicineA extends Medicine{public MedicineA(String name, double price) {super(name, price);}public void accept(Visitor visitor) {visitor.visitor(this);} }具體元素:MedicineB.java
public class MedicineB extends Medicine{public MedicineB(String name, double price) {super(name, price);}public void accept(Visitor visitor) {visitor.visitor(this);} }藥單:Presciption.java
public class Presciption {List<Medicine> list = new ArrayList<Medicine>();public void accept(Visitor visitor){Iterator<Medicine> iterator = list.iterator();while (iterator.hasNext()) {iterator.next().accept(visitor);}}public void addMedicine(Medicine medicine){list.add(medicine);}public void removeMedicien(Medicine medicine){list.remove(medicine);} }客戶端:Client.java
public class Client {public static void main(String[] args) {Medicine a = new MedicineA("板藍(lán)根", 11.0);Medicine b = new MedicineB("感康", 14.3);Presciption presciption = new Presciption();presciption.addMedicine(a);presciption.addMedicine(b);Visitor charger = new Charger();charger.setName("張三");Visitor workerOfPharmacy = new WorkerOfPharmacy();workerOfPharmacy.setName("李四");presciption.accept(charger);System.out.println("-------------------------------------");presciption.accept(workerOfPharmacy);} }運(yùn)行結(jié)果:
設(shè)計(jì)模式系列文章:
Java設(shè)計(jì)模式之創(chuàng)建型:工廠模式詳解(簡單工廠+工廠方法+抽象工廠)
Java設(shè)計(jì)模式之創(chuàng)建型:建造者模式
Java設(shè)計(jì)模式之創(chuàng)建型:單例模式
Java設(shè)計(jì)模式之創(chuàng)建型:原型模式
Java設(shè)計(jì)模式之結(jié)構(gòu)型:適配器模式
Java設(shè)計(jì)模式之結(jié)構(gòu)型:裝飾器模式
Java設(shè)計(jì)模式之結(jié)構(gòu)型:代理模式
Java設(shè)計(jì)模式之結(jié)構(gòu)型:橋接模式
Java設(shè)計(jì)模式之結(jié)構(gòu)型:外觀模式
Java設(shè)計(jì)模式之結(jié)構(gòu)型:組合模式
Java設(shè)計(jì)模式之結(jié)構(gòu)型:享元模式
Java設(shè)計(jì)模式之行為型:策略模式
Java設(shè)計(jì)模式之行為型:模板方法模式
Java設(shè)計(jì)模式之行為型:責(zé)任鏈模式
Java設(shè)計(jì)模式之行為型:觀察者模式
Java設(shè)計(jì)模式之行為型:訪問者模式
Java設(shè)計(jì)模式之行為型:中介者模式
Java設(shè)計(jì)模式之行為型:命令模式
Java設(shè)計(jì)模式之行為型:狀態(tài)模式
Java設(shè)計(jì)模式之行為型:備忘錄模式
Java設(shè)計(jì)模式之行為型:迭代器模式
Java設(shè)計(jì)模式之行為型:解釋器模式
總結(jié)
以上是生活随笔為你收集整理的Java设计模式之行为型:访问者模式的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Java设计模式之行为型:状态模式
- 下一篇: Java设计模式之行为型:解释器模式