- 人人都懂設計模式:從生活中領悟設計模式(Python實現)
- 羅偉富
- 2587字
- 2019-06-19 15:57:02
第3章 中介模式
3.1 從生活中領悟中介模式
3.1.1 故事劇情—找房子問中介
人在江湖漂,豈能總是順心如意?大多數畢業生的第一份工作很難持續兩年以上,與他們一樣,Tony也在一家公司工作了一年半后,換了一個東家。
在北京這個大城市里,換工作基本就意味著換房子。不得不說,找房子是一件煩心而累人的事情!
● 首先,要清楚自己要怎樣的房子:多大面積(多少平方米),什么價位,是否有窗戶,是否有獨立衛生間。
● 然后,要去網上查找各種房源信息,找到最匹配的幾個戶型。
● 之后,還要電話咨詢,過濾虛假信息和過時信息。
● 接著,是最累人的一步,還要實地考察,看看真實的房子與網上的信息是否相符,房間是否有異味,周圍設施是否齊全。這一步你可能會從東城穿越到西城,再來到南城,而后又折騰去北城……想想都累!
● 最后,還要與各種脾性的房東周旋,討價還價。
Tony 想了想,還是找中介算了。在北京這座城市,你幾乎找不到一手房東,因為90%的房源信息都掌握在房屋中介手中!既然都找不到一手房東,還不如找一家正規點的中介。
于是Tony找到了我愛我家,認識了里面的職員Wingle。Wingle問他對房子的要求,Tony說:“18平方米左右,要有獨立衛生間,要有窗戶,最好朝南,有廚房更好!價位在2000元左右。”Wingle立馬就說:“上地西里有一間,但沒有廚房;當代城市家園有兩間,一間主臥,一間次臥,但衛生間是共用的;金隅美和園有一間,比較適合你,但價格會貴一點。”Wingle對房源真是了如指掌啊!說完就帶著我開始看房了……
一天就找到了還算合適的房子。但不得不再次吐槽:北京的房子真的貴得離譜啊,16平方米,精裝修,有朝南窗戶,一個超小(1平方米寬不到)的陽臺,衛生間5人共用,廚房共用,價格是每月2600元。押一付三,加一個月的中介費,一次交了一萬多元,Tony要開始吃土了,內心滴了無數滴血……

3.1.2 用程序來模擬生活
在上面的生活場景中,Tony通過中介來找房子,因為找房子的過程實在太煩瑣了,而且對房源信息也不了解。通過中介,他省去了很多麻煩的細節,合同也是直接跟中介簽的,甚至都不知道房東是誰!
我們通過程序來模擬一下上面找房子的過程。
源碼示例3-1 模擬故事劇情




測試代碼:


輸出結果:


3.2 從劇情中思考中介模式
3.2.1 什么是中介模式
Define an object that encapsulates how a set of objects interact.Mediator promotes loose coupling by keeping objects from referring to each other explicitly,and it lets you vary their interaction independently.
用一個中介對象來封裝一系列的對象交互,中介者使各對象不需要顯式地相互引用,從而使其耦合松散,而且可以獨立地改變它們之間的交互。
在前面的故事劇情中,由中介來承接房客與房東之間的交互過程,可以使得整個過程更加暢通、高效。這在程序中叫作中介模式,中介模式又稱為調停模式。
3.2.2 中介模式設計思想
從故事劇情的示例中我們知道,Tony找房子并不需要與房東進行直接交涉,甚至連房東是誰都不知道,他只需要與中介進行交涉即可,一切都可通過中介完成。這使得他找房子的過程,由如圖3-1所示的狀態變成了如圖3-2所示的狀態,這無疑為他減少了不少麻煩。

圖3-1 沒有中介的找房過程

圖3-2 有中介的找房過程
在很多系統中,多個類很容易相互耦合,形成網狀結構。中介模式的作用就是將這種網狀結構(如圖3-3所示)分離成星型結構(如圖3-4所示)。這樣調整之后,使得對象間的結構更加簡潔,交互更加順暢。

圖3-3 網狀結構

圖3-4 星型結構
3.3 中介模式的模型抽象
3.3.1 代碼框架
從模擬故事劇情(源碼示例3-1)的代碼中,我們可以抽象出中介模式的框架模型。
源碼示例3-2 中介模式的框架模型

3.3.2 類圖
根據上面的示例代碼,我們可以大致地構建出中介模式的類圖,如圖3-5所示。
Mediator 就是中介類,用來協調對象間的交互,如故事劇情中的 HousingAgency。中介類可以有多個具體實現類,如MediatorImplA和MediatorImplB。InteractiveObject是要進行交互的對象,如故事劇情中的HouseOwner和Customer。InteractiveObject可以是互不相干的多個類的對象,也可以是具有繼承關系的相似類。

圖3-5 中介模式的類圖
3.3.3 模型說明
1.設計要點
中介模式主要有以下三個角色,在設計中介模式時要找到并區分這些角色:
(1)交互對象(InteractiveObject):要進行交互的一系列對象。
(2)中介者(Mediator):負責協調各個對象之間的交互。
(3)具體中介者(Mediator):中介的具體實現。
2.中介模式的優缺點
優點:
(1)Mediator 將原本分布于多個對象間的行為集中在一起,作為一個獨立的概念并將其封裝在一個對象中,簡化了對象之間的交互。
(2)將多個調用者與多個實現者之間多對多的交互關系,轉換為一對多的交互關系,一對多的交互關系更易于理解、維護和擴展,大大減少了多個對象之間相互交叉引用的情況。
通過中介找房子給我們帶來了很多的便利,但也存在諸多明顯問題:
(1)很容易遇到黑中介(比如各種不規范和坑,也許你正深陷其中)。
(2)高昂的中介費(給本就受傷的心靈又多補了一刀)。
中介模式也有很多缺點:
(1)中介者承接了所有的交互邏輯,交互的復雜度轉變成了中介者的復雜度,中介者類會變得越來越龐大和復雜,以至于難以維護。
(2)中介者出問題會導致多個使用者同時出問題。
3.4 實戰應用
再舉一個實際應用中的例子。不管是QQ、釘釘這類支持視頻通信的社交軟件,還是51Talk、ABC360這類互聯網在線教育產品,都需要和通信設備(揚聲器、麥克風、攝像頭)進行交互。在移動平臺,各類通信設備一般只會有一個,但在PC端(尤其是Windows系統下),你可能會有多個揚聲器、多個麥克風,甚至多個攝像頭,還可能會在通話的過程中由麥克風A切換到麥克風B。
如何與這些繁雜的設備進行交互呢?聰明的你一定會想:用中介模式啊!對,就是它,我們先看一下程序的設計結構,如圖3-6所示。

圖3-6 設備交互程序的設計結構
圖3-6中的DeviceUtil其實就是中介者,客戶端界面通過DeviceUtil這個中介者與設備進行交互,這樣界面類ClientWidget就不用同時維護三個DeviceMgr的對象,而只要與一個DeviceUtil的對象進行交互就可以了。ClientWidget可通過DeviceUtil枚舉各種類型的設備(揚聲器、麥克風、攝像頭),同時可以通過DeviceUtil讀取和保存當前正在使用的各種類型設備。
這時,可能有讀者要問了:為什么DeviceUtil到DeviceMgr的依賴指向與模型圖不一樣呢?這是因為這個應用中,ClientWidget 與 DeviceMgr 是單向交互的,只有 ClientWidget 調用DeviceMgr,而一般不會有DeviceMgr調用ClientWidget的情況。而模型圖同時支持雙向的交互,InteractiveObject 通過直接依賴與 Mediator 進行交互,而 User 也通過 Mediator 間接地與InteractiveObjectImplA、InteractiveObjectImplB進行交互(如圖3-5中虛線所示)。
下面,我們根據設計圖來實現代碼。
源碼示例3-3 設備管理器





測試代碼:

輸出結果:

3.5 應用場景
(1)一組對象以定義良好但復雜的方式進行通信。產生的相互依賴關系結構混亂且難以理解。
(2)一個對象引用其他很多對象并且直接與這些對象通信,導致難以復用該對象。
(3)想通過一個中間類來封裝多個類中的行為,同時又不想生成太多的子類。