設計模式(十四)中介者模式

劉望舒發表於2017-03-20

相關文章
設計模式(一)設計六大原則
設計模式(二)單例模式的七種寫法
設計模式(三)建造者模式
設計模式(四)簡單工廠模式
設計模式(五)觀察者模式
設計模式(六)代理模式
設計模式(七)裝飾模式
設計模式(八)外觀模式
設計模式(九)模版方法模式
設計模式(十)工廠方法模式
設計模式(十一)策略模式
設計模式(十二)享元模式
設計模式(十三)抽象工廠模式

前言

寫了很多篇設計模式的文章,才發現沒有講關於設計模式的分類,那麼這一篇就補上這一內容,順便帶來中介者模式的講解。並與此前講過的代理模式和外觀模式做對比。

1.設計模式的分類

GoF提出的設計模式總共有23種,根據目的準則分類分為三大類:

  • 建立型模式,共五種:單例模式、工廠方法模式、抽象工廠模式、建造者模式、原型模式。
  • 結構型模式,共七種:介面卡模式、裝飾模式、代理模式、外觀模式、橋接模式、組合模式、享元模式。
  • 行為型模式,共十一種:策略模式、模板方法模式、觀察者模式、迭代器模式、責任鏈模式、命令模式、備忘錄模式、狀態模式、訪問者模式、中介者模式、直譯器模式。

另外隨著設計模式的發展也湧現出很多新的設計模式:它們分別是規格模式、物件池模式、僱工模式、黑板模式和空物件模式等。

2.中介者模式

從前面講到的設計模式的分類中,我們應該得知中介者模式是行為型模式的一種,旨在處理類或物件如何互動及如何分配職責。
中介者模式又叫做調停者模式,名字跟出國留學中介和房產中介是類似的。拿房產中介來說,現在房子買家和房子賣家非常多,如果任由房子買家和房子賣家自由交易,則會導致不同的買家和賣家之間有很多互動,一個買家會和多個賣家進行交涉,同樣的一個賣家也會和多個買家進行交涉。如果在買房的過程中出現糾紛問題,則很難進行解決。就如下圖所示一樣。

設計模式(十四)中介者模式

可以看出房子買家和賣家進行了很多錯綜複雜的互動,並且買家A和賣家B,買家D和賣家D還產生了糾紛,一看到這個圖我們就覺得的暈,當然比我們暈的還有房子買家和賣家。我們在 設計模式(一)設計六大原則這篇文章講過迪米特原則,這個原則所說的就是要儘量減少物件之間的互動,如果兩個物件之間不必彼此直接通訊,那麼這兩個物件就不應當發生任何直接的相互作用,如果其中的一個物件需要呼叫另一個物件的某一個方法的話,可以通過第三者轉發這個呼叫。迪米特原則同樣適用於本場景,我們可以引入第三者也就是房產中介。它的出現不需要買家和賣家進行直接交涉,而是通過房產中介而進行交涉。並且也不容易出現買賣家之間糾紛,因為有中介者房產中介進行第三方監督。如下圖所示。

設計模式(十四)中介者模式

圖中的關係清晰了很多。回到我們軟體開發中,我們為了減少物件之間的互動和耦合,符合迪米特原則,那麼就可以使用中介者模式,先來學習下中介者模式的定義。
中介者模式定義
定義:用一箇中介者物件來封裝一系列的物件互動。中介者使得各物件不需要顯式地相互引用,從而使其鬆散耦合,而且可以獨立地改變它們之間的互動。

中介者模式結構圖如下圖所示。

設計模式(十四)中介者模式

在中介者模式中有如下角色:

  • Mediator:抽象中介者角色,定義了同事物件到中介者物件的介面。
  • ConcreteMediator:具體中介者角色,它從具體的同事物件接收訊息,向具體同事發出命令。
  • Colleague:抽象同事角色,定義了中介者物件介面,它只知道中介者而不知道其他同事物件。
  • ConcreteColleague:具體同事角色,每個具體同事類都知道自己在小範圍內的行為,而不知道它在大範圍內的目的。

中介者模式簡單實現
中介者模式可以拿武俠來舉例,江湖中門派眾多,門派之前因為想法不同會有很多的利益衝突,這樣就會帶來無休止的紛爭。為了江湖的安寧,大家推舉出了一個大家都認可的武林盟主來對江湖紛爭進行調停。
前段時間武當派和峨眉派的的弟子被大力金剛指所殺,大力金剛指是少林派的絕學,因為事情重大,而且少林派實力強大,武當派和峨眉派不能夠直接去少林派去問罪,只能上報武林盟主由武林盟主出面進行調停,如下圖所示。

設計模式(十四)中介者模式

抽象中介者角色
首先我們建立抽象中介者類,在這個例子中,它是一個武林聯盟類,如下所示。

public abstract class WulinAlliance {
    public abstract void notice(String message, United united);
}複製程式碼

notice方法用於向門派傳送通知,其中United為抽象同事類也就是門派類,接下來我們來建立它。

抽象同事角色

public abstract class United {
    protected WulinAlliance wulinAlliance;
    public United(WulinAlliance wulinAlliance){
        this.wulinAlliance=wulinAlliance;
    }
}複製程式碼

門派類(抽象同事類)會在構造方法中得到武林聯盟類(抽象中介者類)。

具體同事角色
具體同事類包括武當派、峨眉派和少林派,如下所示。

/**
 * 具體同事類(武當)
 */

public class Wudang extends United {
    public Wudang(WulinAlliance wulinAlliance) {
        super(wulinAlliance);
    }
    public void sendAlliance(String message) {
        wulinAlliance.notice(message, this);
    }
    public void getNotice(String message) {
        System.out.println("武當收到訊息:" + message);
    }
}

/**
 * 具體同事類(峨眉派)
 */
public class Emei extends United {
    public Emei(WulinAlliance wulinAlliance) {
        super(wulinAlliance);
    }
    public void sendAlliance(String message) {
        wulinAlliance.notice(message, Emei.this);
    }
    public void getNotice(String message) {
        System.out.println("峨眉收到訊息:" + message);
    }
}

/**
 * 具體同事類(少林派)
 */
public class Shaolin extends United {
    public Shaolin(WulinAlliance wulinAlliance) {
        super(wulinAlliance);
    }
    public void sendAlliance(String message){
        wulinAlliance.notice(message,Shaolin.this);
    }
    public void getNotice(String message){
        System.out.println("少林收到訊息:"+message);
    }
}複製程式碼

武當、峨眉和少林類都有兩個方法,其中getNotice方法是自有方法,對於其他的門派(同事類)和武林聯盟(中介者)沒有依賴,只是用來接收武林盟主的通知。sendAlliance方法則是依賴方法,它必須通過武林盟主才能完成行為。

具體中介者角色
具體中介者類則是武林盟主類,如下所示

public class Champions extends WulinAlliance {
    private Wudang wudang;
    private Shaolin shaolin;
    private Emei emei;
    public void setWudang(Wudang wudang) {
        this.wudang = wudang;
    }
    public void setEmei(Emei emei) {
        this.emei = emei;
    }
    public void setShaolin(Shaolin shaolin) {
        this.shaolin = shaolin;
    }
    @Override
    public void notice(String message, United united) {
        if (united == wudang) {
            shaolin.getNotice(message);
        } else if (united == emei) {
            shaolin.getNotice(message);
        } else if (united == shaolin) {
            wudang.getNotice(message);
            emei.getNotice(message);
        }
    }
}複製程式碼

武林盟主需要了解所有的門派,所以需要用setter來持有武當、峨眉和少林的引用。notice方法會根據不同門派發來的訊息,轉而通知給其他的門派。比如武當發來的訊息,武林盟主就會將訊息通知給少林。

客戶端呼叫

public class Client {
    public static void main(String[]args) {
        Champions champions=new Champions();
        Wudang wudang=new Wudang(champions);
        Shaolin shaolin=new Shaolin(champions);
        Emei emei=new Emei(champions);
        champions.setWudang(wudang);
        champions.setShaolin(shaolin);
        champions.setEmei(emei);
        wudang.sendAlliance("武當弟子被少林大力金剛指所殺");
        emei.sendAlliance("峨眉弟子被少林大力金剛指所殺");
        shaolin.sendAlliance("少林弟子絕不會做出這種事情");
    }
}複製程式碼

首先建立武林盟主類Champions 並傳入到各個門派類,接著呼叫武林盟主類的setter方法傳入各個門派類,最後呼叫各個門派的sendAlliance方法通過武林盟主類向其他門派傳送訊息。

輸出結果為:
少林收到訊息:武當弟子被少林大力金剛指所殺
少林收到訊息:峨眉弟子被少林大力金剛指所殺
武當收到訊息:少林弟子絕不會做出這種事情
峨眉收到訊息:少林弟子絕不會做出這種事情

接下來給出這個例子的UML圖,如下所示。

設計模式(十四)中介者模式

中介者模式的優缺點和使用場景
優點
符合迪米特原則,將原有的一對多的依賴變成了一對一的依賴,降低類間的耦合。

缺點
中介者會變得龐大且複雜,原本多個物件直接的相互依賴變成了中介者和多個同事類的依賴關係,同事類越多,中介者的邏輯就越複雜。

使用場景
中介者模式很容易實現呢,但是也容易誤用,不要著急使用,先要思考你的設計是否合理。
當物件之間的互動變多時,為了防止一個類會涉及修改其他類的行為,可以使用中介者模式,將系統從網狀結構變為以中介者為中心的星型結構。

3.代理模式、外觀模式和中介者模式對比

當我們學完中介者模式是不是會覺得和此前講過的代理模式和外觀模式有些類似呢?現在我們一一來將它們進行對比。

代理模式和中介者模式
代理模式是結構型設計模式,它有很多種型別,主要是在訪問物件時引入一定程度的間接性,由於有間接性,就可以附加多種的用途,比如進行許可權控制。中介者模式則是為了減少物件之間的相互耦合。雖然網上有很多代理模式和中介者模式的對比,但是在我看來這兩者實際上並沒有可比性,只是看起來有些類似罷了。
外觀模式和中介者模式
外觀模式主要是以封裝和隔離為主要任務,中介者則是調停同事類之間的關係,因此,中介者具有部分業務的邏輯控制。他們之間的主要區別為:

  • 外觀模式的子系統如果脫離外觀模式還是可以執行的,而中介者模式增加了業務邏輯,同事類不能脫離中介者而獨自存在。
  • 外觀模式中,子系統是不知道外觀類的存在的,而中介者模式中,每個同事類都知道中介者。
  • 外觀模式將子系統的邏輯隱藏,使用者不知道子系統的存在,而中介者模式中,使用者知道同事類的存在。

github原始碼

參考資料
《大話設計模式》
《設計模式之禪》
《Android原始碼設計模式》


歡迎關注我的微信公眾號,第一時間獲得部落格更新提醒,以及更多成體系的Android相關原創技術乾貨。
掃一掃下方二維碼或者長按識別二維碼,即可關注。

設計模式(十四)中介者模式

相關文章