設計模式(十六)——訪問者模式

十四lin發表於2021-02-06

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) 因此,如果一個系統有比較穩定的資料結構,又有經常變化的功能需求,那麼訪問者模式就是比較合適的.

 

僅供參考,有錯誤還請指出!

有什麼想法,評論區留言,互相指教指教。

相關文章