改系列文章是2016年折騰的一個總結,對於這一年中思考和解決的一些問題作一些梳理和總結。javascript
前兩篇文章主要是說了業務邏輯接口還有模塊化的事情。隨着系統內部邏輯單元(多是模塊,也多是爲了解耦拆解出來用來承載職責的類等常見的實現)的增多。勢必會引入另外的一個問題,就是邏輯單元之間的交互增長和邏輯單元之間通訊成本的提升。在iOS架構設計系列之解耦的嘗試之變異的MVVM,一文中咱們在將整個業務邏輯層從MCV向MVVM演變的時候也遇到了這個問題,當時是本着做孽本身造輪子的心態,經過構建EventBus組件來解決。一樣,針對於邏輯單元之間通訊成本增長的問題,也須要尋找一個合適的解決方案。html
在ios多模塊管理一文中,描述一種進行系統模塊拆解和管理的思路。將職能不一樣的業務,拆解成了獨立的模塊。並且每一個模塊經過代碼隔離,作到了互相之間影響的最小化。可是他們之間怎麼交互呢?換種說法就是業務模塊應該暴漏什麼樣的外部接口,以方便其餘業務模塊來調用?java
在終端上業務邏輯主要是圍繞着界面展開的。在iOS中的表現就是各式各樣的ViewController。而在以往的編碼實踐中,所謂業務模塊間的交互就是VC之間的相互調用。咱們常見的是這樣的:linux
UIViewController* aVC = [UIViewController new];
//配置aVC須要參數
....
[self.navigationController pushViewController:aVC animated:YES];複製代碼
或者這樣的ios
UIViewController* aVC = [UIViewController new];
//配置aVC須要的參數
...
[self presentViewController:aVC animated:YES];複製代碼
咱們經過對於指定業務模塊的代碼級引用來調用對方的服務。而這種依賴屬於接口依賴,稍微符合接口隔離的設計。但不是一個符合迪米特法則(最小知識法則)的設計。調用方因爲對於服務提供方有接口依賴,於是就形成了如下的潛在問題:c++
這些問題,咱們經過代碼級別的模塊隔離基本上解決了。正如前文所說,你要真正把模塊之間的交互影響下降的最小,最好的解決方案就是建造『信息孤島』,而信息孤島就會形成模塊之間『雞犬之聲相聞老死不相往來』,這也非我等所願。他們之間還要保持一個最小的通訊,來完成服務的調用。這樣咱們在代碼隔離以後,就要解決兩個問題:git
解決這兩個問題,咱們首先要說一個觀點就是機制與策略分離。咱們但願設計的是一整套可以知足上述要求的協議,其次纔是實現,最後纔是在咱們的APP中的具體應用。這也是我這一年來的一個很是重要的總結。而且在逐漸開源出來的一些庫中也體現着這個設計。具體說一下,所謂機制便是抽象出來的規則,好比:github
f(x)=x^2 x屬於R複製代碼
所謂策略便是在具體場景中的應用,好比當x=2的時候:windows
f(2)=4 x=2複製代碼
很明顯剛纔說的三個層次中協議與實現作成了一個機制與策略分離。而實現與應用又組成了另外的一個機制與策略分離。我比較喜歡這種嵌套的解決方案,你解決了一個通用性的問題,而後嵌套使用,就可以解決更多的問題,只須要付出少許的思惟成本。網絡
協議是問題解決方案的描述,或者說要解決這個問題你們都應該遵照的規則。就像網絡的tcp協議,你要基於tcp通訊你就須要遵循這個協議。
實現是針對於某類環境的實施方案,好比linux上對於TCP的實現還有windows上對於TCP的實現。雖然都是一個協議,可是你們的實現方式不同,有基於c寫的,有基於c++寫的.
而應用是真對具體的問題域提出的實施方案,好比咱們作了一個喲呵校園的聊天軟件使用了tcp進行socket通訊。
那咱們首先要作的就是針對模塊間通訊問題構思一個協議。一個爲了解決模塊間通訊問題你們都遵照的規則。其實關於這個問題在今年下半年,業界飄來一股router風。你們都在模塊化以後的通訊問題上做出了不一樣的嘗試。並且甚至爲此進行了一場博客間的辯論。仔細分析一下,就能發現你們雖各有意見,可是基本上都贊成使用URL的方案來解決這個問題。所爭執的不一樣在於實現方案上的差別。而此處的URL正是咱們所謂的協議部分。
統一資源定位符(或稱統一資源定位器/定位地址、URL地址等[1],英語:Uniform / Universal Resource Locator,常縮寫爲URL),有時也被俗稱爲網頁地址(網址)。如同在網絡上的門牌,是因特網上標準的資源的地址(Address)。它最初是由蒂姆·伯納斯-李發明用來做爲萬維網的地址。如今它已經被萬維網聯盟編制爲因特網標準RFC 1738。
爲什麼如此?
回到最開始咱們描述的問題中第一點: 模塊發現。其實也就是模塊這種資源的定位問題,這個和URL設計的初衷是不謀而合的。URL整套的設計思路就是在總體的互聯網中解決信息孤島,讓各個信息孤島之間可以進行資源發現和資源調用而設計。而咱們目前所要處理的模塊間通訊問題,實際上是這個宏大問題域的一個子集。於是選用URL協議,是一個很是瓜熟蒂落的事情。另一點,這裏真心不必從新造一個相似於URL的協議的輪子出來的。URL協議中可以很是完美的解決這個問題。
其實業界這個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內部本身的事情了,不展開敘述。基本上都是調用庫接口的事情,沒有太多的表述價值。
作個預告吧,也算是對本身的一個敦促,下一篇說一下在DZURLRoute中關於UI堆棧的問題是怎麼解決的。
歡迎關注iOS開發公共帳號 iOS開發知識 :掃描下方二維碼關注