Java设计模式之行为型:访问者模式
背景:
? ? ? ? 去醫(yī)院看病時(shí),醫(yī)生會(huì)給你一個(gè)處方單要你去拿藥,拿藥我們可以分為兩步走:
- (1)去柜臺(tái)交錢,劃價(jià)人員會(huì)根據(jù)處方單上的藥進(jìn)行劃價(jià),交錢。
- (2)去藥房拿藥,藥房工作者同樣根據(jù)處方單給你相對(duì)應(yīng)的藥。
????????這里我們就劃價(jià)和拿藥兩個(gè)步驟進(jìn)行討論,這里有三個(gè)類,處方單(藥)、劃價(jià)人員、藥房工作者。同時(shí)劃價(jià)人員和藥房工作者都各自有一個(gè)動(dòng)作:劃價(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藥}............} }? ? ? ? 這樣的代碼寫法,在藥品種類少的情況沒(méi)什么問(wèn)題,但也存在這么多的 if…else,而且我們可以想象醫(yī)院里的藥是那么多,而且隨時(shí)都會(huì)增加的,增加了藥就要改變劃價(jià)人員和藥房工作者的代碼,這是我們最不希望改變的。那么有沒(méi)有辦法來(lái)解決呢?有,訪問(wèn)者模式提供一中比較好的解決方案。
????????在實(shí)際開(kāi)發(fā)過(guò)程中,我們對(duì)同個(gè)對(duì)象可能存在不同的操作方式,如處方單,劃價(jià)人員要根據(jù)它來(lái)劃價(jià),藥房工作者要根據(jù)它來(lái)給藥。而且可能會(huì)隨時(shí)增加新的操作,如醫(yī)院增加新的藥物,但是這里有兩個(gè)元素是保持不變的,或者說(shuō)很少變:劃價(jià)人員和藥房工作中,變的只不過(guò)是他們的操作。所以我們想如果能夠?qū)⑺麄兊牟僮鞒橄蠡秃昧?#xff0c;這里訪問(wèn)者模式就是一個(gè)值得考慮的解決方案了。
一、什么是訪問(wèn)者模式:
? ? ? ? 訪問(wèn)者模式適用于數(shù)據(jù)結(jié)構(gòu)相對(duì)穩(wěn)定的系統(tǒng),將數(shù)據(jù)結(jié)構(gòu)與基于數(shù)據(jù)的操作進(jìn)行分離,使得添加作用于這些數(shù)據(jù)結(jié)構(gòu)的新操作變得簡(jiǎn)單,并且不需要改變各數(shù)據(jù)結(jié)構(gòu),為不同類型的數(shù)據(jù)結(jié)構(gòu)提供多種訪問(wèn)操作方式,這樣是訪問(wèn)者模式的設(shè)計(jì)動(dòng)機(jī)。
? ? ? ? 除了使新增訪問(wèn)操作變得更加簡(jiǎn)單,也能夠在不修改現(xiàn)有類的層次結(jié)構(gòu)下,定義該類層次結(jié)構(gòu)的操作,并將有關(guān)元素對(duì)象的訪問(wèn)行為集中到一個(gè)訪問(wèn)者對(duì)象中,而不是分散搞一個(gè)個(gè)的元素類中。
? ? ? ?但訪問(wèn)者模式的缺點(diǎn)在于讓增加新的元素類變得困難,每增加一個(gè)新的元素類都意味著要在抽象訪問(wèn)者角色中增加一個(gè)新的抽象操作,并在每一個(gè)具體訪問(wèn)者類中增加相應(yīng)的具體操作,違背了“開(kāi)閉原則”的要求;
? ? ? ? 所以訪問(wèn)者模式適用于對(duì)象結(jié)構(gòu)中很少改變,但經(jīng)常需要在此對(duì)象結(jié)構(gòu)上定義新的操作的系統(tǒng),使得算法操作的增加變得簡(jiǎn)單;或者需要對(duì)一個(gè)對(duì)象結(jié)構(gòu)中進(jìn)行很多不同并且不相關(guān)的操作,并且需要避免讓這些操作污染這些對(duì)象,也不希望在增加新操作時(shí)修改這些類的場(chǎng)景
二、UML結(jié)構(gòu)圖:
- Vistor:抽象訪問(wèn)者,聲明了對(duì)?ConcreteElement 類的一些操作?
- ConcreteVisitor:具體訪問(wèn)者,實(shí)現(xiàn)抽象訪問(wèn)者中聲明的每一個(gè)操作
- Element:抽象元素,定義一個(gè) accept 操作,用于接收具體訪問(wèn)者?
- ConcreteElement:具體元素 ,實(shí)現(xiàn) accept 操作。?
- ObjectStructure:對(duì)象結(jié)構(gòu),提供一個(gè)高層接口來(lái)允許訪問(wèn)者枚舉它的元素
????????從上面的 UML結(jié)構(gòu)圖中我們可以看出,訪問(wèn)者模式主要分為兩個(gè)層次結(jié)構(gòu),一個(gè)是訪問(wèn)者層次結(jié)構(gòu),提供了抽象訪問(wèn)者和具體訪問(wèn)者,主要用于聲明一些操作;一個(gè)是元素層次結(jié)構(gòu),提供了抽象元素和具體元素,主要用于聲明 accept 操作;而對(duì)象結(jié)構(gòu)作為兩者的橋梁,存儲(chǔ)了不同類型的對(duì)象,以便不同的訪問(wèn)者來(lái)訪問(wèn),相同訪問(wèn)者可以以不同的方式訪問(wèn)不同的元素,所以在訪問(wèn)者模式中增加新的訪問(wèn)者無(wú)需修改現(xiàn)有代碼,可擴(kuò)展行強(qiáng)。
????????在訪問(wèn)者模式使用了雙分派技術(shù),所謂雙分派技術(shù)就是在選擇方法的時(shí)候,不僅僅要根據(jù)消息接收者的運(yùn)行時(shí)區(qū)別,還要根據(jù)參數(shù)的運(yùn)行時(shí)區(qū)別。在訪問(wèn)者模式中,客戶端將具體狀態(tài)當(dāng)做參數(shù)傳遞給具體訪問(wèn)者,這里完成第一次分派,然后具體訪問(wèn)者作為參數(shù)的“具體狀態(tài)”中的方法,同時(shí)也將自己this作為參數(shù)傳遞進(jìn)去,這里就完成了第二次分派。雙分派意味著得到的執(zhí)行操作決定于請(qǐng)求的種類和接受者的類型。
二、模式的實(shí)現(xiàn):
????????以上面在醫(yī)院付費(fèi)、取藥為實(shí)例。在這個(gè)實(shí)例中劃價(jià)員和藥房工作者作為訪問(wèn)者,藥品作為訪問(wèn)元素、處方單作為對(duì)象結(jié)構(gòu),所以整個(gè)UML結(jié)構(gòu)圖如下:
抽象訪問(wèn)者: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); }具體訪問(wèn)者:劃價(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());} }具體訪問(wèn)者:藥房工作者、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)建型:工廠模式詳解(簡(jiǎn)單工廠+工廠方法+抽象工廠)
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ì)模式之行為型:訪問(wèn)者模式
Java設(shè)計(jì)模式之行為型:中介者模式
Java設(shè)計(jì)模式之行為型:命令模式
Java設(shè)計(jì)模式之行為型:狀態(tài)模式
Java設(shè)計(jì)模式之行為型:備忘錄模式
Java設(shè)計(jì)模式之行為型:迭代器模式
Java設(shè)計(jì)模式之行為型:解釋器模式
總結(jié)
以上是生活随笔為你收集整理的Java设计模式之行为型:访问者模式的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: Java设计模式之行为型:状态模式
- 下一篇: Java设计模式之行为型:解释器模式