Java設(shè)計模式百例 - 調(diào)停者模式

本文源碼見:https://github.com/get-set/get-designpatterns/tree/master/mediator

調(diào)停者模式(Mediator Pattern)是用來降低多個對象和類之間的通信復(fù)雜性的斧账。這種模式提供了一個調(diào)停者類啃沪,用來充當(dāng)“中心化”或“總線化”的角色蹄溉,與各個對象通信吃靠,從而避免了其他對象之間的互相通信,從而降低了耦合度烙样。

例子

生活中开仰,調(diào)停者模式的例子是相當(dāng)常見的,比如:

  • 一個是講到調(diào)停者模式就避不開的關(guān)于同事之間溝通的例子颂郎。當(dāng)我們身處一個大的團隊中的時候吼渡,如果工作內(nèi)容涉及許多同事,那么再互相溝通顯然成本比較高乓序。比如張三要結(jié)婚請婚假寺酪,手中的工作要暫時交接給李四、王五等五六個同事替劈,分別跟他們單獨溝通多麻煩寄雀,那么直接告知組長或經(jīng)理就好了,由組長或經(jīng)理協(xié)調(diào)一下工作給其他同事即可陨献;
  • 你可能會說盒犹,溝通軟件拉個群通知一下不行嗎,當(dāng)然可以,那這個時候阿趁,這個群就相當(dāng)于一個“調(diào)停者”膜蛔,任何人發(fā)送的消息都匯總到群里,其他群會員都可以收到消息脖阵。
  • 《Java與模式》中提到了關(guān)于WTO這種國際組織的例子皂股,如果各個國家之間互相貿(mào)易,則互相耦合命黔,結(jié)構(gòu)復(fù)雜呜呐,如果都通過一個統(tǒng)一的貿(mào)易組織WTO來協(xié)調(diào),則更加簡單高效悍募。下邊兩個圖也是書中的蘑辑,方便理解:
無調(diào)停者

沒有中心化的貿(mào)易組織時,各個國家直接互相耦合坠宴,為網(wǎng)狀結(jié)構(gòu)洋魂。

有調(diào)停者

有了中心化的貿(mào)易組織后,各個國家不直接溝通喜鼓,統(tǒng)一與WTO耦合副砍,為星型結(jié)構(gòu)。

通過以上例子庄岖,我們可以看出豁翎,調(diào)停者模式的作用在于:通過增加中心化的對象,將網(wǎng)狀的溝通結(jié)構(gòu)變?yōu)樾切徒Y(jié)構(gòu)隅忿,從而降低耦合度心剥,提高靈活性。

下面我們通過上邊提到的第一個例子來看一下調(diào)停者模式如何實現(xiàn):

假設(shè)有一個團隊有兩名開發(fā)熊二背桐、張三优烧,有一名測試?yán)钏模幸幻\維王五链峭。他們都歸一個組長管畦娄,任何人有事情,比如請假熏版、建議或技術(shù)分享纷责,都需要先報告給組長捍掺,由組長統(tǒng)一安排撼短。下面的故事始于張三請假:

首先是團隊組員的抽象類:

TeamMember.java

public abstract class TeamMember {
    // 團隊角色
    public static final String RD = "開發(fā)人員";
    public static final String QA = "測試人員";
    public static final String OP = "運維人員";

    // 僅與自己的組長(調(diào)停者/中心角色)維護引用關(guān)系
    private TechLeader techLeader;
    private String name;
    protected String role;
    
    public TeamMember(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setTechLeader(TechLeader techLeader) {
        this.techLeader = techLeader;
    }

    // 向組長(調(diào)停者/中心角色)發(fā)送信息
    public void reportToLeader(String message) {
        techLeader.memberReport(this, message);
    }

    // 收到來自組長(調(diào)停者/中心角色)的消息
    public void tempTask(String task) {
        System.out.println("[" + role + "]" + name + "收到來自組長的安排: "  + task);
    }

    // 無傷大雅的方法,與模式無關(guān)
    public abstract void dailyWork();
}

這里有幾個與調(diào)停者模式相關(guān)的點:

  • 組員僅與組長(調(diào)停者/中心角色)維護引用關(guān)系挺勿,并不需要知道其他組員曲横;
  • 既然是星型結(jié)構(gòu),那么溝通方式自然是組員與組長之間的雙向溝通,組合可以發(fā)送消息給組長禾嫉,也可以收到來自組長的消息(這里通常通過提供一個回調(diào)方法實現(xiàn)灾杰,比如tempTask)。

那么開發(fā)熙参、測試和運維就是組員的具體實現(xiàn)了:

Developer.java

public class Developer extends TeamMember {

    public Developer(String name) {
        super(name);
        this.role = TeamMember.RD;
    }

    public void dailyWork() {
        System.out.println("我是一個碼農(nóng)艳吠,我經(jīng)常加班寫代碼,困了累了可能寫出bug來孽椰。");
    }

}

Tester.java

public class Tester extends TeamMember {

    public Tester(String name) {
        super(name);
        this.role = TeamMember.QA;
    }

    public void dailyWork() {
        System.out.println("我是一名測試昭娩,我找出bug,確保代碼質(zhì)量黍匾。");
    }

}

Operator.java

public class Operator extends TeamMember {

    public Operator(String name) {
        super(name);
        this.role = TeamMember.OP;
    }

    public void dailyWork() {
        System.out.println("我是一個運維栏渺,保證系統(tǒng)穩(wěn)定運行,如果有線上bug及時回滾锐涯,話說開發(fā)人員寫的程序真不穩(wěn)定磕诊。");
    }

}

技術(shù)組長作為調(diào)停者,也就是星型結(jié)構(gòu)的中心角色:

TechLeader.java

public class TechLeader {
    // 維護有各個組員的引用
    private List<TeamMember> members;

    public TechLeader() {
        members = new ArrayList<TeamMember>();
    }

    public void addTeamMember(TeamMember teamMember) {
        members.add(teamMember);
        teamMember.setTechLeader(this);
    }

    public void memberReport(TeamMember reporter, String message) {
        if (message.contains("請假")) {
            reporter.tempTask("同意纹腌!");
            // 對相關(guān)人員發(fā)送消息或安排其執(zhí)行操作
            for (TeamMember m : members) {
                if (m.getName().equals(reporter.getName())) {
                    continue;
                } else if (m.role.equals(TeamMember.RD)) {
                    m.tempTask(reporter.getName() + "請假了霎终,期間請接手他的開發(fā)工作。");
                } else if (m.role.equals(TeamMember.QA)) {
                    m.tempTask(reporter.getName() + "請假了壶笼,期間請將他的bug交由其他開發(fā)人員處理神僵。");
                } else if (m.role.equals(TeamMember.OP)) {
                    m.tempTask(reporter.getName() + "請假了,期間請將他的線上問題交由其他開發(fā)人員處理覆劈。");
                }
            }
        } else if (message.contains("建議")) {

        } else if (message.contains("技術(shù)分享")) {

        }
    }
}

關(guān)于調(diào)停者有兩個點需要注意:

  • 既然是星型結(jié)構(gòu)保礼,那么調(diào)停者(組長)需要維護所有與之關(guān)聯(lián)的節(jié)點(組員)的引用;通常也需要提供添加節(jié)點的功能责语;
  • 在收到某個節(jié)點的消息后炮障,針對其他相關(guān)節(jié)點發(fā)送消息或調(diào)用其的某些回調(diào)方法(比如例子中給其他角色安排任務(wù))。

最后我們測試一下:

Client.java

public class Client {
    public static void main(String[] args) {
        TeamMember xionger = new Developer("熊二");
        TeamMember zhangsan = new Developer("張三");
        TeamMember lisi = new Tester("李四");
        TeamMember wangwu = new Operator("王五");

        TechLeader leader = new TechLeader();
        leader.addTeamMember(xionger);
        leader.addTeamMember(zhangsan);
        leader.addTeamMember(lisi);
        leader.addTeamMember(wangwu);

        // 張三請假
        zhangsan.reportToLeader("組長坤候,世界很大胁赢,我想去看看,請假兩天~");
    }
}

張三向組長請假兩天白筹,組長收到報告后安排其他組員進行應(yīng)對:

[開發(fā)人員]張三收到來自組長的安排: 同意智末!
[開發(fā)人員]熊二收到來自組長的安排: 張三請假了,期間請接手他的開發(fā)工作徒河。
[測試人員]李四收到來自組長的安排: 張三請假了系馆,期間請將他的bug交由其他開發(fā)人員處理。
[運維人員]王五收到來自組長的安排: 張三請假了顽照,期間請將他的線上問題交由其他開發(fā)人員處理由蘑。

總結(jié)

通過回顧一下上邊的例子闽寡,我們抽取出該設(shè)計模式的關(guān)鍵點:

張三請假
  • 各個節(jié)點之間的網(wǎng)狀耦合關(guān)系變?yōu)橐哉{(diào)停者為中心的星型關(guān)系,其好處是明顯的尼酿,因為隨著節(jié)點數(shù)量的增加爷狈,網(wǎng)狀關(guān)系的復(fù)雜度是以階乘的速度增長的,而且網(wǎng)狀關(guān)系中一個節(jié)點的增刪都會設(shè)計到許多的改動裳擎。
  • 各個節(jié)點之間不在通信涎永,全部與調(diào)停者進行雙向的通信,所以各個節(jié)點僅維護調(diào)停者的引用即可鹿响,而調(diào)停者才需要維護所有節(jié)點的引用土辩,不過注意調(diào)停者并不一定每次都要通知全部的節(jié)點。
  • 通常每個節(jié)點都要有通知調(diào)停者的方法抢野,以及一個用于接收調(diào)停者消息的用于被調(diào)停者回調(diào)的方法拷淘。由于具有通用性和接口性質(zhì),這兩個方法一般放在抽象類中指孤。

調(diào)停者模式的優(yōu)點

  • 松散耦合启涯。調(diào)停者模式通過把多個節(jié)點對象之間的交互封裝到調(diào)停者對象里面,從而使得節(jié)點對象之間松散耦合恃轩,基本上可以做到互補依賴结洼。這樣一來,節(jié)點對象就可以獨立地變化和復(fù)用叉跛,而不再像以前那樣“牽一處而動全身”了松忍。
  • 集中控制交互。多個節(jié)點對象的交互筷厘,被封裝在調(diào)停者對象里面集中管理鸣峭,使得這些交互行為發(fā)生變化的時候,只需要修改調(diào)停者對象就可以了酥艳,當(dāng)然如果是已經(jīng)做好的系統(tǒng)摊溶,那么就擴展調(diào)停者對象,而各個節(jié)點類不需要做修改充石。
  • 多對多變成一對多莫换。沒有使用調(diào)停者模式的時候,節(jié)點對象之間的關(guān)系通常是多對多的骤铃,引入調(diào)停者對象以后拉岁,調(diào)停者對象和節(jié)點對象的關(guān)系通常變成雙向的一對多,這會讓對象的關(guān)系更容易理解和實現(xiàn)惰爬。

調(diào)停者模式的缺點

調(diào)停者模式的一個潛在缺點是喊暖,過度集中化。如果節(jié)點對象的交互非常多补鼻,而且比較復(fù)雜哄啄,當(dāng)這些復(fù)雜性全部集中到調(diào)停者的時候,會導(dǎo)致調(diào)停者對象變得十分復(fù)雜风范,而且難于管理和維護咨跌。通過上邊的例子也可以初見端倪,無論是請假硼婿、建議锌半、技術(shù)分享等等所有的處理都要交給調(diào)停者,心疼組長一分鐘~

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末寇漫,一起剝皮案震驚了整個濱河市刊殉,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌州胳,老刑警劉巖记焊,帶你破解...
    沈念sama閱讀 211,817評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異栓撞,居然都是意外死亡遍膜,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,329評論 3 385
  • 文/潘曉璐 我一進店門瓤湘,熙熙樓的掌柜王于貴愁眉苦臉地迎上來瓢颅,“玉大人,你說我怎么就攤上這事弛说⊥炫常” “怎么了?”我有些...
    開封第一講書人閱讀 157,354評論 0 348
  • 文/不壞的土叔 我叫張陵木人,是天一觀的道長信柿。 經(jīng)常有香客問我,道長醒第,這世上最難降的妖魔是什么角塑? 我笑而不...
    開封第一講書人閱讀 56,498評論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮淘讥,結(jié)果婚禮上圃伶,老公的妹妹穿的比我還像新娘。我一直安慰自己蒲列,他們只是感情好窒朋,可當(dāng)我...
    茶點故事閱讀 65,600評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著蝗岖,像睡著了一般侥猩。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上抵赢,一...
    開封第一講書人閱讀 49,829評論 1 290
  • 那天欺劳,我揣著相機與錄音唧取,去河邊找鬼。 笑死划提,一個胖子當(dāng)著我的面吹牛枫弟,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播鹏往,決...
    沈念sama閱讀 38,979評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼淡诗,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了伊履?” 一聲冷哼從身側(cè)響起韩容,我...
    開封第一講書人閱讀 37,722評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎唐瀑,沒想到半個月后群凶,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,189評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡哄辣,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,519評論 2 327
  • 正文 我和宋清朗相戀三年座掘,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片柔滔。...
    茶點故事閱讀 38,654評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡溢陪,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出睛廊,到底是詐尸還是另有隱情形真,我是刑警寧澤,帶...
    沈念sama閱讀 34,329評論 4 330
  • 正文 年R本政府宣布超全,位于F島的核電站咆霜,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏嘶朱。R本人自食惡果不足惜蛾坯,卻給世界環(huán)境...
    茶點故事閱讀 39,940評論 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望疏遏。 院中可真熱鬧脉课,春花似錦、人聲如沸财异。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,762評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽戳寸。三九已至呈驶,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間疫鹊,已是汗流浹背袖瞻。 一陣腳步聲響...
    開封第一講書人閱讀 31,993評論 1 266
  • 我被黑心中介騙來泰國打工司致, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人聋迎。 一個月前我還...
    沈念sama閱讀 46,382評論 2 360
  • 正文 我出身青樓脂矫,卻偏偏與公主長得像,于是被迫代替她去往敵國和親砌庄。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,543評論 2 349

推薦閱讀更多精彩內(nèi)容