1 測評系統的需求
完成測評系統需求
1) 將觀眾分為男人和女人,對歌手進行測評,當看完某個歌手表演后,得到他們對該歌手不同的評價(評價 有不同的種類,比如 成功、失敗 等)
2) 傳統方案
2 傳統方式的問題分析
1) 如果系統比較小,還是 ok 的,但是考慮系統增加越來越多新的功能時,對代碼改動較大,違反了 ocp 原則, 不利於維護
2) 擴展性不好,比如 增加了 新的人員類型,或者管理方法,都不好做
3) 引出我們會使用新的設計模式 – 訪問者模式
3 訪問者模式基本介紹
1) 訪問者模式(Visitor Pattern),封裝一些作用於某種數據結構的各元素的操作,它可以在不改變數據結構的前提下定義作用於這些元素的新的操作。
2) 主要將數據結構與數據操作分離,解決 數據結構和操作耦合性問題
3) 訪問者模式的基本工作原理是:在被訪問的類里面加一個對外提供接待訪問者的接口
4) 訪問者模式主要應用場景是:需要對一個對象結構中的對象進行很多不同操作(這些操作彼此沒有關聯),同時
需要避免讓這些操作"污染"這些對象的類,可以選用訪問者模式解決
4 訪問者模式的原理類圖
- 對原理類圖的說明-
即(訪問者模式的角色及職責)
1) Visitor 是抽象訪問者,為該對象結構中的 ConcreteElement 的每一個類聲明一個 visit 操作
2) ConcreteVisitor :是一個具體的訪問值 實現每個有 Visitor 聲明的操作,是每個操作實現的部分.
3) ObjectStructure 能枚舉它的元素, 可以提供一個高層的接口,用來允許訪問者訪問元素
4) Element 定義一個 accept 方法,接收一個訪問者對象
5) ConcreteElement 為具體元素,實現了 accept 方法
5 訪問者模式應用實例
應用實例要求
1) 將人分為男人和女人,對歌手進行測評,當看完某個歌手表演后,得到他們對該歌手不同的評價(評價 有不同的種類,比如 成功、失敗 等),請使用訪問者模式來說實現
2) 思路分析和圖解(類圖)
3) 代碼實現
package com.lin.Visitor; public abstract class Person { // 提供一個方法讓訪問者可以訪問 public abstract void accept(Action action); }
package com.lin.Visitor;
//1. 這里我們使用到了雙分派, 即首先在客戶端程序中,將具體狀態作為參數傳遞 Woman 中(第一次分派)
//2. 然后 Woman 類調用作為參數的 "具體方法" 中方法 getWomanResult, 同時將自己(this)作為參數
// 傳入,完成第二次的分派
public class Women extends Person{ @Override public void accept(Action action) { action.getWomenResult(this); } }
package com.lin.Visitor; public class Man extends Person{ @Override public void accept(Action action) { action.getManResult(this); } }
package com.lin.Visitor; public abstract class Action { // 得到男性的測評 public abstract void getManResult(Person person); // 得到女性的測評 public abstract void getWomenResult(Person person); }
package com.lin.Visitor; public class Success extends Action { @Override public void getManResult(Person person) { System.out.println("男性觀眾給的評價是成功!"); } @Override public void getWomenResult(Person person) { System.out.println("女性觀眾給的評價是成功!"); } }
package com.lin.Visitor; public class Fail extends Action { @Override public void getManResult(Person person) { System.out.println("男性觀眾給的評價是失敗!"); } @Override public void getWomenResult(Person person) { System.out.println("女性觀眾給的評價是失敗!"); } }
package com.lin.Visitor; import java.util.LinkedList; import java.util.List; 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); } // 顯示 public void display(Action action) { for (Person person : persons) { person.accept(action); } } }
package com.lin.Visitor; public class Client { public static void main(String[] args) { ObjectStructure objectStructure = new ObjectStructure(); objectStructure.attach(new Women()); objectStructure.attach(new Man()); objectStructure.attach(new Women()); objectStructure.attach(new Man()); Success success = new Success(); objectStructure.display(success); System.out.println("=========================="); Wait wait = new Wait(); objectStructure.display(wait); } }
4) 應用案例的小結-雙分派
-上面提到了雙分派,所謂雙分派是指不管類怎么變化,我們都能找到期望的方法運行。雙分派意味着得到執行的操作取決於請求的種類和兩個接收者的類型
- 以上述實例為例,假設我們要添加一個 Wait 的狀態類,考察 Man 類和 Woman 類的反應,由於使用了雙分派,只需增加一個 Action 子類即可在客戶端調用即可,不需要改動任何其他類的代碼。
6 訪問者模式的注意事項和細節
- 優點
1) 訪問者模式符合單一職責原則、讓程序具有優秀的擴展性、靈活性非常高
2) 訪問者模式可以對功能進行統一,可以做報表、UI、攔截器與過濾器,適用於數據結構相對穩定的系統
- 缺點
1) 具體元素對訪問者公布細節,也就是說訪問者關注了其他類的內部細節,這是迪米特法則所不建議的, 這樣造成了具體元素變更比較困難
2) 違背了依賴倒轉原則。訪問者依賴的是具體元素,而不是抽象元素
3) 因此,如果一個系統有比較穩定的數據結構,又有經常變化的功能需求,那么訪問者模式就是比較合適的.
僅供參考,有錯誤還請指出!
有什么想法,評論區留言,互相指教指教。