你好,歡迎來到IOS教程網

 Ios教程網 >> IOS編程開發 >> IOS開發基礎 >> iOS架構設計解耦的嘗試之模塊間通信

iOS架構設計解耦的嘗試之模塊間通信

編輯:IOS開發基礎

該系列文章是2016年折騰的一個總結,對於這一年中思考和解決的一些問題做一些梳理和總結。

前兩篇文章主要是說了業務邏輯接口還有模塊化的事情。隨著系統內部邏輯單元(可能是模塊,也可能是為了解耦拆解出來用來承載職責的類等常見的實現)的增多。勢必會引入另外的一個問題,就是邏輯單元之間的交互增加和邏輯單元之間通信成本的提高。在iOS架構設計系列之解耦的嘗試之變異的MVVM,一文中我們在將整個業務邏輯層從MCV向MVVM演變的時候也遇到了這個問題,當時是本著作孽自己造輪子的心態,通過構建EventBus組件來解決。同樣,針對於邏輯單元之間通信成本增加的問題,也需要尋找一個合適的解決方案。

問題場景描述

在ios多模塊管理一文中,描述一種進行系統模塊拆解和管理的思路。將職能不同的業務,拆解成了獨立的模塊。而且每個模塊通過代碼隔離,做到了互相之間影響的最小化。但是他們之間怎麼交互呢?換種說法就是業務模塊應該暴漏什麼樣的外部接口,以方便其他業務模塊來調用?

在終端上業務邏輯主要是圍繞著界面展開的。在iOS中的表現就是各式各樣的ViewController。而在以往的編碼實踐中,所謂業務模塊間的交互就是VC之間的相互調用。我們常見的是這樣的:

UIViewController* aVC = [UIViewController new];
//配置aVC需要參數
....
[self.navigationController pushViewController:aVC animated:YES];

或者這樣的

UIViewController* aVC = [UIViewController new];
//配置aVC需要的參數
...
[self presentViewController:aVC animated:YES];

我們通過對於指定業務模塊的代碼級引用來調用對方的服務。而這種依賴屬於接口依賴,稍微符合接口隔離的設計。但不是一個符合迪米特法則(最小知識法則)的設計。調用方由於對於服務提供方有接口依賴,因而就造成了以下的潛在問題:

  1. 鏈接過程中必須引入服務提供方所在的模塊,無法提供打包過程中動態下掉某些業務模塊的需求。

  2. 服務提供方類接口變動,將直接調用方。

  3. 調用方知道了服務提供方的太多有冗余信息。

這些問題,我們通過代碼級別的模塊隔離基本上解決了。正如前文所說,你要真正把模塊之間的交互影響降低的最小,最好的解決方案就是建造『信息孤島』,而信息孤島就會造成模塊之間『雞犬之聲相聞老死不相往來』,這也非我等所願。他們之間還要保持一個最小的通信,來完成服務的調用。這樣我們在代碼隔離之後,就要解決兩個問題:

  1. 模塊發現,就是說我一個模塊怎麼被其他模塊發現,或者說我一個模塊做些什麼事情,外部模塊使用的時候,才能知道有我的存在。

  2. 服務調用,模塊做為服務提供方,需要能夠真實的提供所標稱的服務。

機制與策略分離

解決這兩個問題,我們首先要說一個觀點就是機制與策略分離。我們希望設計的是一整套能夠滿足上述要求的協議,其次才是實現,最後才是在我們的APP中的具體應用。這也是我這一年來的一個非常重要的總結。並且在逐漸開源出來的一些庫中也體現著這個設計。具體說一下,所謂機制即是抽象出來的規則,比如:

f(x)=x^2 x屬於R

所謂策略即是在具體場景中的應用,比如當x=2的時候:

f(2)=4 x=2

很明顯剛才說的三個層次中協議與實現做成了一個機制與策略分離。而實現與應用又組成了另外的一個機制與策略分離。我比較喜歡這種嵌套的解決方案,你解決了一個通用性的問題,然後嵌套使用,就能夠解決更多的問題,只需要付出少量的思維成本。

協議是問題解決方案的描述,或者說要解決這個問題大家都應該遵守的規則。就像網絡的tcp協議,你要基於tcp通信你就需要遵循這個協議。

實現是針對於某類環境的實施方案,比如linux上對於TCP的實現還有windows上對於TCP的實現。雖然都是一個協議,但是大家的實現方式不一樣,有基於c寫的,有基於c++寫的.

而應用是真對具體的問題域提出的實施方案,比如我們做了一個喲呵校園的聊天軟件使用了tcp進行socket通信。

解決方案設計

模塊間通信協議URL

那我們首先要做的就是針對模塊間通信問題構思一個協議。一個為了解決模塊間通信問題大家都遵守的規則。其實關於這個問題在今年下半年,業界飄來一股router風。大家都在模塊化之後的通信問題上作出了不同的嘗試。而且甚至為此進行了一場博客間的辯論。仔細分析一下,就能發現大家雖各有意見,但是基本上都同意使用URL的方案來解決這個問題。所爭執的不同在於實現方案上的差異。而此處的URL正是我們所謂的協議部分。

統一資源定位符(或稱統一資源定位器/定位地址、URL地址等[1],英語:Uniform / Universal Resource Locator,常縮寫為URL),有時也被俗稱為網頁地址(網址)。如同在網絡上的門牌,是因特網上標准的資源的地址(Address)。它最初是由蒂姆·伯納斯-李發明用來作為萬維網的地址。現在它已經被萬維網聯盟編制為因特網標准RFC 1738。

為何如此?

回到最開始我們描述的問題中第一點: 模塊發現。其實也就是模塊這種資源的定位問題,這個和URL設計的初衷是不謀而合的。URL整套的設計思路就是在整體的互聯網中解決信息孤島,讓各個信息孤島之間能夠進行資源發現和資源調用而設計。而我們目前所要處理的模塊間通信問題,其實是這個宏大問題域的一個子集。因而選用URL協議,是一個非常順理成章的事情。另外一點,這裡真心沒必要重新造一個類似於URL的協議的輪子出來的。URL協議中能夠非常完美的解決這個問題。

在iOS中基於URL協議的模塊間通信實現DZURLRoute

其實業界這個route的實現已經有千千萬萬了,為啥我還要再寫一個?一個是因為原有的一些庫的模型和我所想象的不吻合,一個是因為我實現不想削足適履去適配他們的模型。所以本著造輪子的作孽心態還是自己寫。其實也不是非常復雜。

URL協議解決了模塊發現的問題,但是是個靜態的txt,並不具備exe的能力。我們可以通過定義一個類似於:

yoho://innerfuction/viewcontroller/showuserinfo?uid=22&xx=33

來讓一個模塊對外宣稱支持顯示用戶詳細信息的服務。但是我們要如何使用這個服務呢?很多Route的實現是通過URL直接將對應的ViewController返回,然後由調用方再去調用接口配置ViewController,而後調用方進行push或者present。而我認為這種方式不是很合理,做為調用發應該盡可能少的知道服務提供方的信息。服務怎麼被彈起,應該是由服務方自己決定的,而不是調用方。最好只知道一個URL還有支持什麼樣的服務就好了,最好能把交互接口精簡、精簡、再精簡。

而思考了一下很多route庫之所以沒能夠做到模塊只對外暴漏URL就可以的一個很重要的原因,就是在ViewController被彈出的時候,iOS需要一個調起的ViewController

UIViewController* aVC = [UIViewController new];
//配置aVC需要參數
....
[self.navigationController pushViewController:aVC animated:YES];

就是必須知道當前界面是在哪裡,你才能去push下一個界面。只有知道這個self.navigationController上下文信息才行。所以很多事情只能在調用方來處理。我覺得這種方式制約著被調用方需要拿到服務提供方的一個實例才行。每一個問題背後都有一個解決方案。於是我在自己造的輪子中使用了全局UI堆棧的方式解決了這個問題。

通過構造了被調用的上下文信息類DZURLRequestContext,用於攜帶調用方的上下文信息來解決這個問題。上下文信息中攜帶了當前UI的堆棧信息,能夠方便定位用哪個VC做為起點,來彈出下一個頁面。當然使用這個context還有另外一個原因就是因為URL中能夠傳輸的參數類型是受限的,只能傳輸NSString類型,對於一個實例則不能傳輸。為了傳輸實例參數也需要這樣的一個context環境。

這樣在調用一個頁面的服務的時候,就能夠做到如下所示極致簡單:

[[DZURLRoute defaultRoute] routeURL:DZURLRouteQueryLink(kYHURLSacnQRCode, @{})];

當然該庫首先是解決了通過URL調用的問題,而後才是上面說的這些優化問題。同時,也針對很多不同的應用場景提供了解決方案。更加具體的信息可以參考DZURLRoute。

而具體的應用問題,就是APP內部自己的事情了,不展開敘述。基本上都是調用庫接口的事情,沒有太多的表述價值。

Others

做個預告吧,也算是對自己的一個敦促,下一篇說一下在DZURLRoute中關於UI堆棧的問題是怎麼解決的。

14.png

  1. 上一頁:
  2. 下一頁:
蘋果刷機越獄教程| IOS教程問題解答| IOS技巧綜合| IOS7技巧| IOS8教程
Copyright © Ios教程網 All Rights Reserved