1.初識(shí)中介者模式
用一個(gè)中介對(duì)象來封裝一系列的對(duì)象交互。中介者使得各對(duì)象不需要顯式地相互引用,從而使其耦合松散,而且可以獨(dú)立的改變它們之間的交互。

- Mediator:中介者接口。在里面定義各個(gè)同事之間交互需要的方法,可以是公共的通訊方法,比如changed方法,大家都用,也可以是小范圍的交互方法。
ConcreteMediator:具體中介者實(shí)現(xiàn)對(duì)象。它需要了解并維護(hù)各個(gè)同事對(duì)象,并負(fù)責(zé)具體的協(xié)調(diào)各同事對(duì)象的交互關(guān)系。
Colleague:同事類的定義,通常實(shí)現(xiàn)成為抽象類,主要負(fù)責(zé)約束同事對(duì)象的類型,并實(shí)現(xiàn)一些具體同事類之間的公共功能,比如:每個(gè)具體同事類都應(yīng)該知道中介者對(duì)象,也就是具體同事類都會(huì)持有中介者對(duì)象,就可以定義到這個(gè)類里面。
ConcreteColleague:具體的同事類,實(shí)現(xiàn)自己的業(yè)務(wù),在需要與其它同事通訊的時(shí)候,就與持有的中介者通信,中介者會(huì)負(fù)責(zé)與其它的同事交互。
2.體會(huì)中介者模式
2.1 場景問題——使用電腦來看電影
為了演示,考慮一個(gè)稍微具體點(diǎn)的功能。在日常生活中,我們經(jīng)常使用電腦來看電影,把這個(gè)過程描述出來,這里僅僅考慮正常的情況,也就是有主板的情況,簡化后假定會(huì)有如下的交互過程:
- step1.首先是光驅(qū)要讀取光盤上的數(shù)據(jù),然后告訴主板,它的狀態(tài)改變了
- step2.主板去得到光驅(qū)的數(shù)據(jù),把這些數(shù)據(jù)交給CPU進(jìn)行分析處理
- step3.CPU處理完后,把數(shù)據(jù)分成了視頻數(shù)據(jù)和音頻數(shù)據(jù),通知主板,它處理完了
- step4.主板去得到CPU處理過后的數(shù)據(jù),分別把數(shù)據(jù)交給顯卡和聲卡,去顯示出視頻和發(fā)出聲音
當(dāng)然這是一個(gè)持續(xù)的、不斷重復(fù)的過程,從而形成不間斷的視頻和聲音,具體的運(yùn)行過程不在討論之列,假設(shè)就有如上簡單的交互關(guān)系就可以了。也就是說想看電影,把光盤放入光驅(qū),光驅(qū)開始讀盤,就可以看電影了
2.2 使用模式的解決方案

3.理解中介者模式
3.1 認(rèn)識(shí)中介者模式
3.1.1 中介者模式的功能
中介者的功能非常簡單,就是封裝對(duì)象之間的交互。如果一個(gè)對(duì)象的操作會(huì)引起其它相關(guān)對(duì)象的變化,或者是某個(gè)操作需要引起其它對(duì)象的后續(xù)或連帶操作,而這個(gè)對(duì)象又不希望自己來處理這些關(guān)系,那么就可以找中介者,把所有的麻煩扔給它,只在需要的時(shí)候通知中介者,其它的就讓中介者去處理就可以了。
反過來,其它的對(duì)象在操作的時(shí)候,可能會(huì)引起這個(gè)對(duì)象的變化,也可以這么做。最后對(duì)象之間就完全分離了,誰都不直接跟其它對(duì)象交互,那么相互的關(guān)系,全部被集中到中介者對(duì)象里面了,所有的對(duì)象就只是跟中介者對(duì)象進(jìn)行通信,相互之間不再有聯(lián)系。
把所有對(duì)象之間的交互都封裝在中介者當(dāng)中,無形中還得到另外一個(gè)好處,就是能夠集中的控制這些對(duì)象的交互關(guān)系,這樣有什么變化的時(shí)候,修改起來就很方便。
3.1.2 需要Mediator接口嗎
有沒有使用Mediator接口的必要,取決于是否會(huì)提供多個(gè)不同的中介者實(shí)現(xiàn)。如果中介者實(shí)現(xiàn)只有一個(gè)的話,而且預(yù)計(jì)中也沒有需要擴(kuò)展的要求,那么就可以不定義Mediator接口,讓各個(gè)同事對(duì)象直接使用中介者實(shí)現(xiàn)對(duì)象;如果中介者實(shí)現(xiàn)不只一個(gè),或者預(yù)計(jì)中有擴(kuò)展的要求,那么就需要定義Mediator接口,讓各個(gè)同事對(duì)象來面向中介者接口編程,而無需關(guān)心具體的中介者實(shí)現(xiàn)。
3.1.3 同事關(guān)系
在中介者模式中,要求這些類都要繼承相同的類,也就是說,這些對(duì)象從某個(gè)角度講是同一個(gè)類型,算是兄弟對(duì)象。
正是這些兄弟對(duì)象之間的交互關(guān)系很復(fù)雜,才產(chǎn)生了把這些交互關(guān)系分離出去,單獨(dú)做成中介者對(duì)象,這樣一來,這些兄弟對(duì)象就成了中介者對(duì)象眼里的同事。
3.1.4 同事和中介者的關(guān)系
中介者對(duì)象和同事對(duì)象之間是相互依賴的。
3.1.5 如何實(shí)現(xiàn)同事和中介者的通信
一種實(shí)現(xiàn)方式是在Mediator接口中定義一個(gè)特殊的通知接口,作為一個(gè)通用的方法,讓各個(gè)同事類來調(diào)用這個(gè)方法。
另外一種實(shí)現(xiàn)方式是可以采用觀察者模式,把Mediator實(shí)現(xiàn)成為觀察者,而各個(gè)同事類實(shí)現(xiàn)成為Subject,這樣同事類發(fā)生了改變,會(huì)通知Mediator。 Mediator在接到通知過后,會(huì)與相應(yīng)的同事對(duì)象進(jìn)行交互。
3.1.6 中介者模式的調(diào)用順序示意圖

3.2 廣義中介者
3.2.1 標(biāo)準(zhǔn)的中介者模式在實(shí)際使用中的困難
1.是否有必要為同事對(duì)象定義一個(gè)公共的父類?
大家都知道,Java是單繼承的,為了使用中介者模式,就讓這些同事對(duì)象繼承一個(gè)父類,這是很不好的;再說了,這個(gè)父類目前也沒有什么特別的公共功能,也就是說繼承它也得不到多少好處。
在實(shí)際開發(fā)中,很多相互交互的對(duì)象本身是沒有公共父類的,強(qiáng)行加上一個(gè)父類,會(huì)讓這些對(duì)象實(shí)現(xiàn)起來特別別扭。
2.同事類有必要持有中介者對(duì)象嗎?
同事類需要知道中介者對(duì)象,以便當(dāng)它們發(fā)生改變的時(shí)候,能夠通知中介者對(duì)象,但是否需要作為屬性,并通過構(gòu)造方法傳入,這么強(qiáng)的依賴關(guān)系呢?
也可以有簡單的方式去通知中介對(duì)象,比如把中介對(duì)象做成單例,直接在同事類的方法里面去調(diào)用中介者對(duì)象。
3.是否需要中介者接口?
在實(shí)際開發(fā)中,很常見的情況是不需要中介者接口的,而且中介者對(duì)象也不需要?jiǎng)?chuàng)建很多個(gè)實(shí)例,因?yàn)橹薪檎呤怯脕矸庋b和處理同事對(duì)象的關(guān)系的,它一般是沒有狀態(tài)需要維護(hù)的,因此中介者通??梢詫?shí)現(xiàn)成單例。
4.中介者對(duì)象是否需要持有所有的同事?
雖說中介者對(duì)象需要知道所有的同事類,這樣中介者才能與它們交互。但是是否需要做為屬性這么強(qiáng)烈的依賴關(guān)系,而且中介者對(duì)象在不同的關(guān)系維護(hù)上,可能會(huì)需要不同的同事對(duì)象的實(shí)例,因此可以在中介者處理的方法里面去創(chuàng)建、或者獲取、或者從參數(shù)傳入需要的同事對(duì)象。
5.中介者對(duì)象只是提供一個(gè)公共的方法,來接受同事對(duì)象的通知嗎?
從示例就可以看出來,在公共方法里,還是要去區(qū)分到底是誰調(diào)過來,這還是簡單的,還沒有去區(qū)分到底是什么樣的業(yè)務(wù)觸發(fā)調(diào)用過來的,因?yàn)椴煌臉I(yè)務(wù),引起的與其它對(duì)象的交互是不一樣的。
因此在實(shí)際開發(fā)中,通常會(huì)提供具體的業(yè)務(wù)通知方法,這樣就不用再去判斷到底是什么對(duì)象,具體是什么業(yè)務(wù)了。
3.2.2 對(duì)標(biāo)準(zhǔn)的中介者模式在實(shí)際使用中的改進(jìn)
基于上面的考慮,在實(shí)際應(yīng)用開發(fā)中,經(jīng)常會(huì)簡化中介者模式,來使開發(fā)變得簡單,比如有如下的簡化
- 1)通常會(huì)去掉同事對(duì)象的父類,這樣可以讓任意的對(duì)象,只要需要相互交互,就可以成為同事;
- 2)還有通常不定義Mediator接口,把具體的中介者對(duì)象實(shí)現(xiàn)成為單例;
- 3)另外一點(diǎn)就是同事對(duì)象不再持有中介者,而是在需要的時(shí)候直接獲取中介者對(duì)象并調(diào)用;中介者也不再持有同事對(duì)象,而是在具體處理方法里面去創(chuàng)建、或者獲取、或者從參數(shù)傳入需要的同事對(duì)象。
把這樣經(jīng)過簡化、變形使用的情況稱為廣義中介者。
3.2.3 廣義中介者示例——部門與人員
3.2.3.1 部門和人員的關(guān)系
是多對(duì)多的
3.2.3.2 問題的出現(xiàn)
想想部門和人員的功能交互,舉幾個(gè)常見的功能:
- 1)部門被撤銷
- 2)部門之間進(jìn)行合并
- 3)人員離職
- 4)人員從一個(gè)部門調(diào)職到另外一個(gè)部門
想想要實(shí)現(xiàn)這些功能,按照前面的設(shè)計(jì),該怎么做呢?
- 1)系統(tǒng)運(yùn)行期間,部門被撤銷了,就意味著這個(gè)部門不存在了,可是原來這個(gè)部門下所有的人員,每個(gè)人員的所屬部門中都有這個(gè)部門呢,那么就需要先通知所有的人員,把這個(gè)部門從它們的所屬部門中去掉,然后才可以清除這個(gè)部門。
- 2)部門合并,是合并成一個(gè)新的部門呢,還是把一個(gè)部門并入到另一個(gè)部門?如果是合并成一個(gè)新的部門,那么需要把原有的兩個(gè)部門撤銷,然后再新增一個(gè)部門;如果是把一個(gè)部門合并到另一個(gè)部門里面,那就是撤銷掉一個(gè)部門,然后把這個(gè)部門下的人員移動(dòng)到這個(gè)部門。不管是那種情況,都面臨著需要通知相應(yīng)的人員進(jìn)行更改這樣的問題。
- 3)人員離職了,反過來就需要通知他所屬于的部門,從部門的擁有人員的記錄中去除掉這個(gè)人員。
- 4)人員調(diào)職,同樣需要通知相關(guān)的部門,先從原來的部門中去除掉,然后再到新的部門中添加上。
看了上述的描述,感覺如何?
麻煩的根源在什么地方呢?仔細(xì)想想,對(duì)了,麻煩的根源就在于部門和人員之間的耦合,這樣導(dǎo)致操作人員的時(shí)候,需要操作所有相關(guān)的部門,而操作部門的時(shí)候又需要操作所有相關(guān)的人員,使得部門和人員攪和在了一起。
3.2.3.3 中介者來解決
找到了根源就好辦了,采用中介者模式,引入一個(gè)中介者對(duì)象來管理部門和人員之間的關(guān)系,就能解決這些問題了。
如果采用標(biāo)準(zhǔn)的中介者模式,想想上面提出的那些問題點(diǎn)吧,就知道實(shí)現(xiàn)起來會(huì)很別扭。因此采用廣義的中介者來解決,這樣部門和人員就完全解耦了,也就是說部門不知道人員,人員也不知道部門,它們完全分開,它們之間的關(guān)系就完全由中介者對(duì)象來管理了。

3.3 中介者模式的優(yōu)缺點(diǎn)
- 松散耦合
- 集中控制交互
- 多對(duì)多變成一對(duì)多
- 過度集中化
4.思考中介者模式
4.1 中介者模式的本質(zhì)
封裝交互
4.2 何時(shí)選用
- 1)如果一組對(duì)象之間的通信方式比較復(fù)雜,導(dǎo)致相互依賴、結(jié)構(gòu)混亂,可以采用中介者模式,把這些對(duì)象相互的交互管理起來,各個(gè)對(duì)象都只需要和中介者交互,從而使得各個(gè)對(duì)象松散耦合,結(jié)構(gòu)也更清晰易懂
- 2)如果一個(gè)對(duì)象引用很多的對(duì)象,并直接跟這些對(duì)象交互,導(dǎo)致難以復(fù)用該對(duì)象。可以采用中介者模式,把這個(gè)對(duì)象跟其它對(duì)象的交互封裝到中介者對(duì)象里面,這個(gè)對(duì)象就只需要和中介者對(duì)象交互就可以了