設計模式(十九)訪問者模式
阿新 • • 發佈:2018-11-09
訪問者模式
訪問者模式的場景:
公司老闆要看報表, 公司有銷售,財務,研發,每個員工有自己獨特的資訊,最常見的做法是: 定義抽象員工,並定義模板方法,每一個員工實現自己角色的個性化方法,通過模板方法report來訪問。
比如是這樣的,
public abstract Employee{ private String name; private String telephone; public String getBasicInfo(){ return name+"tel: "+telephone; } public abstract String getSpecialInfo(); public void report(){ System.out.println(getBasicInfo()); System.out.println(getSpecialInfo()); } }
這樣每個員工繼承基類,各自實現自己的特殊資訊就可以了。
但是這樣有一個問題,報表的輸出格式全由report() 方法控制, 如果有一天老闆想換種報表格式,比如不想看員工姓名了怎麼辦?
是不是要修改基類的report方法了, 開閉原則指導要對修改關閉對擴充套件開放, 如果直接改基類report也會出現問題,比如今天一個格式出報表,改下基類,明天又換個格式改報表,又要改基類,還想同時出兩份格式不同的報表怎麼辦?
所以就引出本文中提到的訪問者模式。
首先分析,要為每一個角色個性化自己的報表方法,可以把角色作為引數傳給一個單獨的訪問者物件Visitor, 由Visitor利用多型來實現個性化輸出。並且由Visitor 去負責產生報表格式。 物件和Visitor是弱耦合的,不是繼承關係。 這種實現方式便於擴充套件,需要產生不同格式的報表,多新增一個Visitor完成這個特殊的功能就可以了。
訪問者模式的角色
-
Visitor 抽象訪問者。 宣告哪些元素可以訪問
-
ConcreteVisitor 具體訪問者。 訪問到一個類後,具體怎麼做,做什麼
-
Element 抽象元素。 介面或者抽象類,宣告接受哪一類訪問者訪問。程式中通過accept方法中的引數來定義
-
ConcreteElement 具體元素,實現accept方法
-
ObjectStructure 結構物件。 元素產生者
public abstract class Element { public abstract void doSomething(); public abstract void accept(IVisitor visitor); } public class ConcreteElement1 extends Element{ @Override public void doSomething() { //業務處理 } //允許哪個訪問者訪問 @Override public void accept(IVisitor visitor) { visitor.visit(this); } } public class ConcreteElement2 extends Element{ @Override public void doSomething() { //業務處理 } //允許哪個訪問者訪問 @Override public void accept(IVisitor visitor) { visitor.visit(this); } } public interface IVisitor{ public void visit(ConcreteElement1 el1); public void visit(ConcreteElement2 el2); } public class Visitor implements IVisitor{ @Override public void visit(ConcreteElement1 el1) { el1.doSomething(); } @Override public void visit(ConcreteElement2 el2) { el2.doSomething(); } } public class ObjectStructure{ public static Element createElement(){ Random rand=new Random(); if(rand.nextInt(100)>50){ return new ConcreteElement1(); }else{ return new ConcreteElement2(); } } } public class Client{ public static void main(String[] args){ for(int i=0;i<10;i++){ Element e=ObjectStructure.createElement(); e.accept(new Visitor()); } } }
訪問者模式優點
- 符合單一原則。 Visitor負責報表的實現,Employee及其子類負責資料載入
- 優秀的擴充套件性。 如果需要不同的格式報表,直接新增Visitor或者在Visitor新增方法就可以了。
- 靈活性高。
訪問者模式的缺點
- 具體元素對訪問者公佈細節。 違反迪米特法則
- 具體元素變更比較困難
- 違背了依賴倒置原則。 放棄介面,依賴實現類,擴充套件比較困難。
訪問者模式的使用場景
- 需要遍歷不同的物件, 迭代器模式只能訪問同類或同介面的資料,訪問者模式是對迭代器模式的 擴充,可以根據不同的物件,執行不同的操作。