C++ 設(shè)計(jì)模式 —— 17.中介者模式

  • 中介者模式:一種行為型設(shè)計(jì)模式

  • 應(yīng)用場(chǎng)景:
    一般來(lái)說(shuō)叶撒,大型的軟件在開(kāi)發(fā)的過(guò)程中會(huì)使用模塊化的開(kāi)發(fā)方式禁荒。以我現(xiàn)在參與的項(xiàng)目舉例羽资,完整的軟件包括交互场航、檢索、算路袍榆、引導(dǎo)等等多個(gè)模塊胀屿,這樣有利于模塊之間的獨(dú)立開(kāi)發(fā),提高整體開(kāi)發(fā)效率包雀。
    但是無(wú)論模塊之間如何獨(dú)立宿崭,總是會(huì)有需要模塊間通信的時(shí)候。
    例如用戶通過(guò)界面交互才写,發(fā)起檢索葡兑,這個(gè)過(guò)程需要交互與檢索兩模塊間通信奖蔓,將用戶的意圖由交互模塊傳遞至檢索模塊。
    這就會(huì)產(chǎn)生一個(gè)擴(kuò)展性上的問(wèn)題讹堤。如果模塊有很多吆鹤,且每個(gè)模塊都向外提供了唯一的接收消息的接口,那么一個(gè)模塊如果需要與其他所有模塊通信洲守,則需要記錄其他所有模塊的通信接口疑务,同理每個(gè)模塊都要記錄所有與自己相關(guān)聯(lián)的模塊的通信接口。首先這就是一件很冗余并降低開(kāi)發(fā)效率的事情梗醇。
    其次知允,一旦有一天,某個(gè)模塊修改了自己所提供的通信接口叙谨,或者增加了一個(gè)新模塊温鸽,尤其是后者,在公司項(xiàng)目發(fā)展過(guò)程中是很正常的事情手负。那么所有模塊關(guān)于其他模塊通信接口的記錄可能都需要修改或增加涤垫,這在擁有龐大模塊數(shù)量的項(xiàng)目中,是很恐怖的事情竟终。
    此時(shí)可以考慮中介者模式蝠猬。
  • 舉例:
    假設(shè)現(xiàn)在有3個(gè)模塊A B CMessageABA B間雙向傳遞的一個(gè)消息衡楞,同理還有MessageAC吱雏、MessageBC敦姻。
    引入一個(gè)中介者瘾境,它手中記錄著各模塊的通信接口通信協(xié)議,協(xié)議即是一種約定镰惦,A B兩模塊間約定通過(guò)MessageAB來(lái)傳遞某種信息迷守,即是這兩模塊間的一個(gè)協(xié)議。模塊間通信都將消息發(fā)給中介者旺入,中介者無(wú)需關(guān)注消息具體用來(lái)傳遞什么信息兑凿,只需要關(guān)注協(xié)議雙方,通過(guò)正確的通信接口轉(zhuǎn)發(fā)消息茵瘾,確保消息的轉(zhuǎn)發(fā)對(duì)象正確即可礼华,而消息的解析處理在接收模塊內(nèi)部進(jìn)行。
    這樣如果有通信接口修改或增加新模塊拗秘,協(xié)議雙方將新協(xié)議告知中介者記錄即可圣絮。
  • 實(shí)現(xiàn)方式:
    中介者對(duì)象中需要保存所有需轉(zhuǎn)發(fā)消息的模塊指針作為成員變量。
    各模塊類(lèi)中也需要保存負(fù)責(zé)轉(zhuǎn)發(fā)消息的中介者指針作為成員變量雕旨。
    各模塊類(lèi)中需要實(shí)現(xiàn)發(fā)送與接收消息的方法扮匠。模塊發(fā)送消息時(shí)調(diào)用中介者的轉(zhuǎn)發(fā)接口捧请,中介者確定接收目標(biāo)后調(diào)用目標(biāo)的接收接口
    下面代碼中為了簡(jiǎn)化協(xié)議棒搜,我統(tǒng)一了各模塊接收消息的接口名稱(chēng)疹蛉。

以下是中介者模式的簡(jiǎn)單代碼實(shí)現(xiàn)
#include <iostream>
using namespace std;

/*
 * 約定三種協(xié)議
 * MessageAB是AB模塊間通信的消息
 * MessageAC是AC模塊間通信的消息
 * MessageBC是BC模塊間通信的消息
 */
enum Message
{
    MessageAB,
    MessageAC,
    MessageBC,
};

class ModuleBase;
//中介者基類(lèi),提供轉(zhuǎn)發(fā)接口
class MediatorBase
{
public:
    virtual void Transmit(Message enMessage, ModuleBase* pFrom) = 0;
};

//模塊基類(lèi)力麸,實(shí)現(xiàn)向外發(fā)消息的方法
class ModuleBase
{
public:
    ModuleBase(MediatorBase* pMediator):m_pMediator(pMediator){}
    //模塊向外發(fā)消息的方法
    void SendMessage(Message enMessage)
    {
        m_pMediator->Transmit(enMessage, this);
    }

    //模塊接收消息的接口可款,由模塊自己實(shí)現(xiàn)
    virtual void vNotify(Message enMessage) = 0;

protected:
    //每個(gè)模塊內(nèi)都有一份負(fù)責(zé)分發(fā)消息的中介者
    MediatorBase* m_pMediator;
};

//模塊A
class ModuleA : public ModuleBase
{
public:
    ModuleA(MediatorBase* pMediator):ModuleBase(pMediator){}
    virtual void vNotify(Message enMessage)
    {
        switch(enMessage)
        {
        case MessageAB:
            cout << "ModuleA get MessageAB from ModuleB" << endl;
            break;
        case MessageAC:
            cout << "ModuleA get MessageAC from MoudleC" << endl;
            break;
        default:
            break;
        }
    }
};

//模塊B
class ModuleB : public ModuleBase
{
public:
    ModuleB(MediatorBase* pMediator):ModuleBase(pMediator){}
    virtual void vNotify(Message enMessage)
    {
        switch(enMessage)
        {
        case MessageAB:
            cout << "ModuleB get MessageAB from ModuleA" << endl;
            break;
        case MessageBC:
            cout << "ModuleB get MessageBC form ModuleC" << endl;
            break;
        default:
            break;
        }
    }
};

//模塊C
class ModuleC : public ModuleBase
{
public:
    ModuleC(MediatorBase* pMediator):ModuleBase(pMediator){}
    virtual void vNotify(Message enMessage)
    {
        switch(enMessage)
        {
        case MessageAC:
            cout << "ModuleC get MessageAC from ModuleA" << endl;
            break;
        case MessageBC:
            cout << "ModuleC get MessageBC form ModuleB" << endl;
            break;
        default:
            break;
        }
    }
};

//中介者實(shí)現(xiàn)類(lèi)
class ConcreteMediator : public MediatorBase
{
public:
    ConcreteMediator():m_pModA(NULL),m_pModB(NULL),m_pModC(NULL){}
    void Transmit(Message enMessage, ModuleBase* pFrom)
    {
        switch(enMessage)
        {
        case MessageAB:
            {
                ModuleA* pFromWhere = dynamic_cast<ModuleA*>(pFrom);
                //能通過(guò)dynamic_cast轉(zhuǎn)換為不為NULL的指針,可確認(rèn)指針指向的子類(lèi)類(lèi)型
                if(NULL != pFromWhere)
                {
                    m_pModB->vNotify(enMessage);
                }
                else
                {
                    m_pModA->vNotify(enMessage);
                }
            }
            break;
        case MessageAC:
            {
                ModuleA* pFromWhere = dynamic_cast<ModuleA*>(pFrom);
                if(NULL != pFromWhere)
                {
                    m_pModC->vNotify(enMessage);
                }
                else
                {
                    m_pModA->vNotify(enMessage);
                }
            }
        case MessageBC:
            {
                ModuleB* pFromWhere = dynamic_cast<ModuleB*>(pFrom);
                if(NULL != pFromWhere)
                {
                    m_pModC->vNotify(enMessage);
                }
                else
                {
                    m_pModB->vNotify(enMessage);
                }
            }
        }
    }

    void SetModuleA(ModuleBase* pModuleA){m_pModA = pModuleA;}
    void SetModuleB(ModuleBase* pModuleB){m_pModB = pModuleB;}
    void SetModuleC(ModuleBase* pModuleC){m_pModC = pModuleC;}

private:
    ModuleBase* m_pModA;
    ModuleBase* m_pModB;
    ModuleBase* m_pModC;
};
主函數(shù)中的使用
int main()
{
    MediatorBase* pMediator = new ConcreteMediator();

    //為所有模塊指定中介者
    ModuleBase* pModA = new ModuleA(pMediator);
    ModuleBase* pModB = new ModuleB(pMediator);
    ModuleBase* pModC = new ModuleC(pMediator);

    //為中介者設(shè)定模塊
    ConcreteMediator* pConcreteMediator = dynamic_cast<ConcreteMediator*>(pMediator);
    pConcreteMediator->SetModuleA(pModA);
    pConcreteMediator->SetModuleB(pModB);
    pConcreteMediator->SetModuleC(pModC);

    //各模塊間開(kāi)始互發(fā)消息
    pModA->SendMessage(MessageAC);
    pModB->SendMessage(MessageBC);
    pModC->SendMessage(MessageBC);

    return 0;
}
控制臺(tái)輸出結(jié)果
ModuleC get MessageAC from ModuleA
ModuleC get MessageBC form ModuleB
ModuleB get MessageBC form ModuleC

如有錯(cuò)誤,歡迎指正

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末克蚂,一起剝皮案震驚了整個(gè)濱河市筑舅,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌陨舱,老刑警劉巖翠拣,帶你破解...
    沈念sama閱讀 212,383評(píng)論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異游盲,居然都是意外死亡误墓,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,522評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門(mén)益缎,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)谜慌,“玉大人,你說(shuō)我怎么就攤上這事莺奔⌒婪叮” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 157,852評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵令哟,是天一觀的道長(zhǎng)恼琼。 經(jīng)常有香客問(wèn)我,道長(zhǎng)屏富,這世上最難降的妖魔是什么晴竞? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,621評(píng)論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮狠半,結(jié)果婚禮上噩死,老公的妹妹穿的比我還像新娘。我一直安慰自己神年,他們只是感情好已维,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,741評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著已日,像睡著了一般垛耳。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,929評(píng)論 1 290
  • 那天艾扮,我揣著相機(jī)與錄音既琴,去河邊找鬼。 笑死泡嘴,一個(gè)胖子當(dāng)著我的面吹牛甫恩,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播酌予,決...
    沈念sama閱讀 39,076評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼磺箕,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了抛虫?” 一聲冷哼從身側(cè)響起松靡,我...
    開(kāi)封第一講書(shū)人閱讀 37,803評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎建椰,沒(méi)想到半個(gè)月后雕欺,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,265評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡棉姐,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,582評(píng)論 2 327
  • 正文 我和宋清朗相戀三年屠列,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片伞矩。...
    茶點(diǎn)故事閱讀 38,716評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡笛洛,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出乃坤,到底是詐尸還是另有隱情苛让,我是刑警寧澤,帶...
    沈念sama閱讀 34,395評(píng)論 4 333
  • 正文 年R本政府宣布湿诊,位于F島的核電站狱杰,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏枫吧。R本人自食惡果不足惜浦旱,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,039評(píng)論 3 316
  • 文/蒙蒙 一宇色、第九天 我趴在偏房一處隱蔽的房頂上張望九杂。 院中可真熱鬧,春花似錦宣蠕、人聲如沸例隆。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,798評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)镀层。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間唱逢,已是汗流浹背吴侦。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,027評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留坞古,地道東北人备韧。 一個(gè)月前我還...
    沈念sama閱讀 46,488評(píng)論 2 361
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像痪枫,于是被迫代替她去往敵國(guó)和親织堂。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,612評(píng)論 2 350

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