image
我們使用的電腦,你完成的任何一個功能都需要cpu、內存、顯卡、鍵盤、顯示器等這些零件相互調用才能完成功能,如果讓這些零件之間直接互相調用,那麼他們之間的關系可能如下圖所示,非常凌亂復雜:
但是電腦開發商並沒有讓這些零件之間相互直接調用,而是通過主板來統一協調,這樣每個零件只需要按照主板要求的接口去完成功能即可,然後把處理完成的數據傳遞給主板就可以了,不需要了解其他零件,此時結構如如下:
面向對象設計鼓勵將行為分布到各個對象中。這種分布可能會導致對象間有許多連接。 在最壞的情況下 ,每一個對象都知道其他所有對象。
雖然將一個系統分割成許多對象通常可以增強可復用性 , 但是對象間相互連接的激增又會 降低其可復用性。大量的相互連接使得一個對象似乎不太可能在沒有其他對象的支持下工作—--系統表現為一個不可分割的整體。而且對系統的行為進行任何較大的改動都十分困難, 因為行為被分布在許多對象中。結果是你可能不得不定義很多子類以定制系統的行為。今天講解的中介者模式就是為了解決這個問題而誕生的。
定義
用一個中介對象來封裝一系列的對象交互。中介者使各對象不需要顯式地相互引用,從而使其耦合松散,而且可以獨立地改變它們之間的交互。
中介者模式解決的困境就是多個對象之間的相互引用導致的緊耦合,通過引入一個中介者,原來互相引用的對象都變成同事類,他們之間現在沒有任何關系,只和中介者交互,這樣就實現了解耦。
這樣以後如果增加或者修改任何同事類的功能,也只需要修改中介者,不需要修改其他同事類。
說白了中介者模式把對象之間的多對多轉換成了一對多,從而降低耦合。
UML圖及說明
上圖是標准的中介者模式,其實在實際使用的時候會做一定程度的變形使用。下面加以說明:
1、是否需要mediator接口
接口就是用來實現面向接口編程,封裝有多個中介者實現時的帶來的變化。所以如果有多個中介者,那麼就需要mediator接口,反之則不需要,實際場景中一般很少會有多個中介者,所以可以不需要接口
2、是否需要定義同事類的父類
其實在實際開發中,這些同事類不可能抽象為同一個父類,他們之間很少有共同性。所以沒必要給他們定義一個共同父類
3、colleague和mediator是否需要相互持有
因為colleague和mediator需要相互通知自己的變化讓對方做出反應,所以在標准實現中他們之間是相互持有的。其實可以把mediator做成單例,讓同事類直接調用就好了。
同樣的道理,mediator也沒必要持有colleague,可以通過方法的參數吧colleague傳遞到mediator。
4、mediator只需要提供一個公共方法嗎
在實際開發中,我們不僅要區分是哪個同事類傳遞過來的信息,還需要區分不同業務類型,所以需要根據實際需求定義多個公共方法。
5、mediator和colleague如何通信
標准模式裡面是互相引用,然後告知對方,其實還可以使用觀察者模式,讓兩者之間互相觀察,有了變化就可以通知對方
實際場景運用
1、需求分析
假設我們使用電腦播放視頻,把步驟分為如下幾步
光驅讀取光盤內容,把讀取到的內容傳遞給主板
主板得到內容,交給cpu處理
cpu處理完畢,把處理後的數據傳遞給主板
主板把數據傳遞給顯卡,顯卡顯示視頻(忽略了顯示器顯示這個步驟)
如果不適用中介者模式(加入主板),那麼每個對象(零件)之間需要互相引用,耦合增加,導致復用修改困難。
3、代碼實現
定義三個同事類:cpu、CDDriver、videoCard
#import @interface CPU : NSObject -(void)executeData:(NSMutableString *)data; @end ====== #import "CPU.h" #import "mainBoard.h" @implementation CPU -(void )executeData:(NSMutableString *)data{ [data appendString:@"+經過cpu處理"]; [[mainBoard shareInstance] handleData:data dataSource:self]; } @end
#import "CDDriver.h" #import "mainBoard.h" @implementation CDDriver -(void)readCD{ NSString *data = @"BBC地球探索之旅"; NSMutableString *mStr = [[NSMutableString alloc]initWithString:data]; [[mainBoard shareInstance] handleData:mStr dataSource:self]; } @end
#import "VideoCard.h" @implementation VideoCard -(void )executeData:(NSMutableString *)data{ [data appendString:@"+經過顯卡處理"]; NSLog(@"開始播放視頻:“%@",data); } @end
定義mediator類
#import @interface mainBoard : NSObject +(instancetype)shareInstance; -(void)handleData:(NSMutableString *)data dataSource:(id)source; @end ======================= #import "mainBoard.h" #import "CPU.h" #import "CDDriver.h" #import "VideoCard.h" static mainBoard *instance = nil; @implementation mainBoard +(instancetype)shareInstance{ static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ if(instance == nil){ instance = [[self alloc]init]; } }); return instance; } -(void)handleData:(NSMutableString *)data dataSource:(id)source{ if ([source isKindOfClass:[CDDriver class]]){ CPU *cpu = [CPU new]; [cpu executeData:data]; }else if ([source isKindOfClass:[CPU class]]){ VideoCard *video = [VideoCard new]; [video executeData:data]; } } @end
客戶端調用:
#import #import "CDDriver.h" int main(int argc, const char * argv[]) { @autoreleasepool { // insert code here... CDDriver *cd = [CDDriver new]; [cd readCD]; } return 0; }
通過上面的例子可以看到三個同事類的交互對象只有mainboard這個mediator類,它們之間互相不知道,減少了耦合。這裡演示的只是這三個類實現的一個功能,實際情況是這三個類存在多種功能,比如播放音頻,看文檔。每個功能都需要這三個類之間的相互交互,那麼在任何需要這些功能的地方,都需要引入這三個類,引入的地方越多,這三個類和其他類的耦合度就越高。
假設這三個類需要更改,那麼牽一發動全身,其他所有引用這些類的地方都要改,但是如果引入中介者模式,則不會有這些問題。
優缺點
1、 減少了子類生成Mediator將原本分布於多個對象間的行為集中在一起 。 改 變 這 些 行 為,只需生成 Meditor的子類即可。這樣各個 Colleage 類可被重用。
2、 它將各 Colleague 解耦 Mediator 有利於各 Coleague 間的松耦合 . 你 可 以 獨 立 的 改 變 和 復
用各 Colleague 類和 Mediator 類。
3、它 簡 化 了 對 象 協 議 用 Mediator 和各 Colleague 間 的 一 對 多 的 交 互 來 代 替 多 對 多 的 交 互 。一對多的關系更易於理解、維護和擴展。
4、它對對象如何協作進行了抽象 將中介作為一個獨立的概念並將其封裝在一個對象中,
使你將注意力從對象各自本身的行為轉移到它們之間的交互上來。這有助於弄清楚一個系統 中的對象是如何交互的。
5、它使控制集中化。中介者模式將交互的復雜性變為中介者的復雜性。因為中介者封裝了協議 , 它可能變得比任一個colleague都復雜。 這可能使得中介者自身成為一個難於維護的龐然大物。
何時使用
一組對象以定義良好但是復雜的方式進行通信。產生的相互依賴關系結構混亂且難以理解。
一個對象引用其他很多對象並且直接與這些對象通信,導致難以復用該對象。
想定制一個分布在多個類中的行為,而又不想生成太多的子類。
Demo下載
中介者模式demo