首先來看看wiki上對中介者模式的解釋:
In software engineering, the mediator pattern defines an object that encapsulates how a set of objects interact. This pattern is considered to be a behavioral pattern due to the way it can alter the program's running behavior.
在軟體工程中,中介模式定義了一個物件,該物件封裝了一組物件是如何互動的。這種模式被認為是一個行為模式,因為它可以改變程式的執行行為。
在一個專案裡,我們開發的程式是由大量的類來組成的,隨著程式功能的不斷增加,類和類之間的依賴關係也跟著趨於複雜,而中介者模式便能解決這個問題。
這種複雜的依賴關係,在iOS開發當中常用的MVC模式中定義的ViewController(VC)就是典型的代表。一般情況下一個頁面便對應程式碼中的一個VC,而一箇中等規模的軟體至少會有幾十個的頁面,對應的就是幾十個VC。(當然你也可以說複用VC,但是在正常情況下,VC的複用場景會有這麼多麼?這裡我們不考慮複用VC的情況。)而要管理這些VC之間的關係是一件非常繁瑣的事情,我們要處理各個VC之間的關係,每當一個VC要跳轉到另外個VC,我們需要包含新的VC的標頭檔案,於是有的起銜接作用的頁面中包含了大量的其他VC的標頭檔案。同時每當產品經理要求修改一些頁面的邏輯關係,我們又需要對這些標頭檔案和對應的程式碼進行修改,想想我就頭大。類似下圖:
而使用中介者模式可以非常好地去解決這個問題。實現一個管理VC關係的功能類,這個類的作用是給每個VC繫結一個URL,當需要開啟某一個新的VC時,通過功能類的openURL介面傳入新VC的URL即可。發起openURL的一方不需要去依賴新的VC,只需要和功能類建立聯絡,類似下圖:
Mediator類的作用就相當於一個路由器(Router),將客戶發起的URL請求轉移到對應的類。我們還可以利用iOS應用本身的特點,以及Objective-C語言的特性,給這個Router增加一些功能: 在一些特殊情況下,開啟不同的頁面(VC)之後,還需要傳遞一些命令到這個VC,讓其做一些特殊的操作,於是可以通過配置URL的引數,再通過引數名和引數值傳遞給新VC:
[MRRouter openURL:@"scheme://test?aa=11&bb=22"];
複製程式碼
更方便的,可以直接用字典來傳遞引數:
[MRRouter openURL:@"scheme://test3" parameters:@{@"ccc":@"333",@"ddd":@"444"}];
複製程式碼
於是還需要建立一個對映表,來建立URL和VC之間的對映關係,每增加一個VC,就在這個表裡頭增加一個對應關係。這裡可以利用Objective-C的Runtime來動態地根據類名去對映,簡化我們的日常編碼操作,具體的實現就不在這裡詳細描述了,有興趣可以去這裡看具體的實現。
還可以定義一個預設的openURL操作:
[MRRouter sharedInstance].defaultExecutingBlock = ^(id object, NSDictionary *parameters) {
[self.navigationController pushViewController:object animated:YES];
};
複製程式碼
這樣在一般情況下開啟一個新的頁面,不需要引入任何標頭檔案,也不需要建立對映關係,只需要呼叫Router的openURL介面。
通過以中介者模式為基礎的Router管理頁面的額外好處,就是可以簡化服務端push需要開啟新頁面的操作,當有一個新的活動,要開啟一個對應的頁面,只需要把這個頁面的URL通過push傳送給客戶端,客戶端直接傳遞給Router即可,省略了大量的if/else的邏輯判斷。
總結:
中介者模式的使用不光用在VC的管理,當功能中出現了類似“多對多”的複雜的物件群時,就可以用到它來管理這些物件。當然,再次之前,你需要考慮的不應該是開始使用中介者模式,而是考慮這個功能的設計是否合理。 使用中介者模式雖然降低了各個物件之間的耦合,減少了物件之間邏輯的複雜度,但是這個複雜度在一定程度上轉移到了Mediator類中,因此Mediator類的功能維護需要謹慎處理。 以上舉例中的所有程式碼可以在這裡下載,歡迎各種star/request/issue。