1. 程式人生 > 其它 >【設計模式從入門到精通】14-訪問者模式

【設計模式從入門到精通】14-訪問者模式

訪問者模式

筆記來源:尚矽谷Java設計模式(圖解+框架原始碼剖析)

目錄

訪問者模式

1、測評系統

**完成測評系統需求

  • 1)將人、觀眾分為男人和女人,對歌手進行測評,當看完某個歌手錶演後,得到他們對該歌手不同的評價(評價有不同的種類,比如成功、失敗等)
  • 2)傳統方案

傳統方式的問題分析

  • 1)如果系統比較小,還是 ok 的,但是考慮系統增加越來越多新的功能時,對程式碼改動較大,違反了 OCP 原則,不利於維護
  • 2)擴充套件性不好,比如增加了新的人員型別,或者管理方法,都不好做
  • 3)引出我們會使用新的設計模式——訪問者模式

2、基本介紹

  • 1)訪問者模式(Visitor Pattern),封裝一些作用於某種資料結構的各元素的操作,它可以在不改變資料結構的前提下定義作用於這些元素的新的操作
  • 2)主要將資料結構與資料操作分離,解決資料結構和操作耦合性問題
  • 3)訪問者模式的基本工作原理是:在被訪問的類裡面加一個對外提供接待訪問者的介面
  • 4)訪問者模式主要應用場景是:需要對一個物件結構中的物件進行很多不同操作(這些操作彼此沒有關聯),同時需要避免讓這些操作“汙染“這些物件的類,可以選用訪問者模式解決

原理類圖

訪問者角色及職責

  • Visitor抽象訪問者:為該物件結構中每個ConcreteElement類宣告一個visit
    操作
  • ConcreteVisitor具體訪問者:實現Visitor中宣告的操作
  • ObjectStructure物件結構:能列舉它的元素,提供一個高層介面,允許訪問者訪問元素
  • Element抽象元素:定義一個accept方法,接受一個訪問者物件
  • ConcreteElement具體元素:實現了accept方法

3、訪問者模式實現測評系統

UML 類圖

核心程式碼

/**
 * 抽象訪問者
 */
public abstract class Action {
    public abstract void getManResult(Man man);

    public abstract void getWomanResult(Woman woman);
}
/**
 * 具體訪問者
 */
public class Success extends Action {
    @Override
    public void getManResult(Man man) {
        System.out.println("男生給了通過");
    }

    @Override
    public void getWomanResult(Woman woman) {
        System.out.println("女生給了通過");
    }
}
public class Fail extends Action {
    @Override
    public void getManResult(Man man) {
        System.out.println("男生給了不通過");
    }

    @Override
    public void getWomanResult(Woman woman) {
        System.out.println("女生給了不通過");
    }
}
public class Wait extends Action {
    @Override
    public void getManResult(Man man) {
        System.out.println("男生給了待定");
    }

    @Override
    public void getWomanResult(Woman woman) {
        System.out.println("女生給了待定");
    }
}
/**
 * 抽象元素
 */
public abstract class Person {
    public abstract void accept(Action action);
}
/**
 * 具體元素
 */
public class Man extends Person {
    @Override
    public void accept(Action action) {
        action.getManResult(this);
    }
}
public class Woman extends Person {
    @Override
    public void accept(Action action) {
        action.getWomanResult(this);
    }
}
/**
 * 物件結構
 */
public class ObjectStructure {
    private List<Person> personList = new ArrayList<>();

    public void attach(Person p) {
        personList.add(p);
    }

    public void detach(Person p) {
        personList.remove(p);
    }

    public void display(Action action) {
        for (Person person : personList) {
            person.accept(action);
        }
    }
}

呼叫測試

ObjectStructure objectStructure = new ObjectStructure();
objectStructure.attach(new Man());
objectStructure.attach(new Woman());
objectStructure.display(new Success());
System.out.println("============");
objectStructure.display(new Fail());
System.out.println("============");
objectStructure.display(new Wait());
//男生給了通過
//女生給了通過
//============
//男生給了不通過
//女生給了不通過
//============
//男生給了待定
//女生給了待定

4、雙分派

該例中我們使用到了雙分派

  • 第一次分派:首先在客戶端程式中,將具體狀態作為引數傳遞Woman
  • 第二次分派:然後Woman類呼叫作為引數的具體方法getWomanResult,同時將自己this作為引數傳入

所謂雙分派是指不管類怎麼變化,我們都能找到期望的方法執行

雙分派意味著得到執行的操作取決於請求的種類和兩個接收者的型別

以上述例項為例,假設我們要新增一個Wait的狀態類,考察Man類和Woman類的反應

由於使用了雙分派,只需增加一個 Action 子類即可在客戶端呼叫即可,不需要改動任何其他類的程式碼

5、訪問者模式的注意事項和細節

優點

  • 1)訪問者模式符合單一職責原則、讓程式具有優秀的擴充套件性、靈活性非常高
  • 2)訪問者模式可以對功能進行統一,可以做報表、UI、攔截器與過濾器,適用於資料結構相對穩定的系統

缺點

  • 1)具體元素對訪問者公佈細節,也就是說訪問者關注了其他類的內部細節,這是迪米特法則所不建議的,這樣造成了具體元素變更比較困難
  • 2)違背了依賴倒轉原則。訪問者依賴的是具體元素,而不是抽象元素
  • 3)因此,如果一個系統有比較穩定的資料結構,又有經常變化的功能需求,那麼訪問者模式就是比較合適的