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

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

前言

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

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相關原創技術干貨。
掃一掃下方二維碼或者長按識別二維碼,即可關注。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,488評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,034評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,327評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,554評論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,337評論 6 404
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,883評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 42,975評論 3 439
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,114評論 0 286
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,625評論 1 332
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,555評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,737評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,244評論 5 355
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 43,973評論 3 345
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,362評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,615評論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,343評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,699評論 2 370

推薦閱讀更多精彩內容