iOS元件化方案(二)

發表於2017-01-06

概述

這是iOS元件化方案-總結的第二篇,在本文中我實現了Target-Action方案的Demo,並與第一篇介紹的protocol方案做出對比

如果沒有看過我第一篇protocol元件化方案的同學,可以先去下載我那篇文章中提供的Demo,方便理解我本文的詳述以及瞭解我Demo中實現的業務場景,傳送門iOS元件化方案-總結的第一篇

Target-Action方案

國際慣例先上Demo(下載主工程就好了哈,如果不能理解可以把所有業務模組都下載下來,Casa也提供了官方Demo,我第一篇文章中提供了傳送門)

Target-Action方案主工程

Target-Action方案商品詳情業務Category元件地址

Target-Action方案商品詳情業務模組地址

Target-Action方案確認訂單Category元件地址

Target-Action方案確認訂單業務模組地址

Target-Action方案CTMediator地址因Casa沒有把CTMediator做成公有庫,所以我是直接拷貝過來做成我的私有庫了。

實施

如何把模組做成私有pods我這裡就不介紹了,想知道的可以看我第一篇元件化介紹文章。我這裡只拿確認訂單模組舉例

確認訂單模組是個單獨的project,為了避免其他模組呼叫確認訂單模組需引入整個模組,這裡又做了一個確認訂單業務Category的私有元件如下圖

113839983-b4e2a523b31e7da4
icon

TAConfirmOrderBusinessCategory即是確認訂單模組對外提供服務的入口,我們的業務場景是商品詳情模組立即購買進入確認訂單模組,確認訂單模組提交訂單後返回商品詳情模組,同時得到通知下單成功,所以上圖中入參提供了ConfirmComplete的Block,下圖是TAConfirmOrderBusinessCategory.m中的實現

OK,TAConfirmOrderBusinessCategory實現完了,我們來看下TAConfirmOrder模組,模組中定義一個Target_TAConfirmOrder具體實現如下圖

既然TAConfirmOrderBusinessCategoryTAConfirmOrder是2個project,那category是如何呼叫到Target_TAConfirmOrder的呢?其實很簡單,我想看這篇文章的人大部分都知道,無非就是NSClassFromString ,performSelector這些方法,不知道的可以閱讀原始碼

到這裡我都沒有貼過架構圖或者講過原理,只是貼了一部分程式碼和講述如何實現,為什麼?其實元件化原理很簡單,簡單到比當初學習UITableView容易多了,我的Demo即原理,如果還是看不明白可以自行google一輪或者在評論區提問.

Target_Action VS Protocol方案

1.是否需要註冊?

  • Target_Action方案不需要註冊
  • Protocol方案需要在啟動的時候向CRProtocolManager註冊

    Target_Action很好的利用了runtime特性,減少註冊這一步,不過對於即將切到Swift的同學就有點尷尬了。

    在上篇提供的Protocol方案Demo中,在向CRProtocolManager註冊服務的是例項物件而非Class,這樣確實會造成記憶體常駐,但是無傷大雅,熟悉runtime的同學應該都知道第一次呼叫某個類或物件的方法,會構建出類物件,所以無論你用Class註冊還是例項物件註冊類物件都在,拋開類物件對於一個不掛任何property的例項物件所佔用的記憶體是很小的。當然你可能會問既然都差不多你為什麼註冊例項而不是註冊Class,註冊的ServiceProvider例項物件在有些情況下可以記錄一些狀態,當然這只是極少數情況下出現的,你如果真要把ServiceProvider當單例物件用,我還是強烈建議註冊Class

    不過我不認為ServiceProvider需要向中介軟體註冊有邏輯上的問題,區別只是可省可不省

2.依賴關係

Target_Action方案中商品詳情模組依賴TAConfirmOrderBusinessCategory元件來獲取確認訂單模組的服務

Protocol方案中商品詳情模組需要依賴CRConfirmOrderServiceProtocol通過CRProtocolManager元件獲取提供服務的例項物件,同時確認訂單模組也依賴CRConfirmOrderServiceProtocol來註冊服務

乍一看Protocol方案依賴關係好像對業務產生了侵入因為呼叫方和實現方都同時依賴了CRConfirmOrderServiceProtocol,其實CRConfirmOrderServiceProtocol應當屬於確認訂單模組的一部分,把他獨立出來只是為了避免呼叫方需要直接引用實現方,這個依賴在架構圖中體現的應該是虛線而不是實線。試想如果Target_Action方案不用runtime,那BusinessCategory也需要直接依賴Target。利用runtime中NSProtocolFromString也可以解決對CRConfirmOrderServiceProtocol的依賴,只是造成一定量的硬編碼不夠優雅。(提一下,雖然runtime在一些特定場景給我們開發帶來一些意想不到的奇效,但是runtime跳過了編譯器檢查,有時候排除bug比較艱難,所以還是慎用)

另Protocol方案中商品詳情模組同時依賴CRConfirmOrderServiceProtocolCRProtocolManager而Target_Action方案中商品詳情模組僅依賴TAConfirmOrderBusinessCategory,依賴關係如下圖

123839983-a7f16f41fe5d5871
icon

Protocol方案橫向依賴了2者,Target_Action方案縱向依賴。Target_Action設計的更優異

3.可讀性、硬編碼

Target_Action在Category中將常規引數打包成字典,在Target處再把字典拆包成常規引數,這造成了一定量的硬編碼,不過在現實開發中,一個模組一個模組提供的category通常是一個人寫的,所以造成的影響微乎其微,但是給其他閱讀程式碼的人帶來一些不便,甚至同一個人寫Cagetory、Target的時候也需要在2個project不停切換檢視之前在Target中定義的函式名

Protocol方案中0硬編碼,可讀性更高。

在這裡提一下Url註冊方案,Url註冊方案我覺得最大的問題是大量的硬編碼,可讀性很差,維護性也很差,對文件的依賴度很高,而且需要有人不停督促文件的更新。我想很多同學對此都深有體會,每個專案第一版的介面文件相對比較詳細、全面,隨著版本迭代更新,某個介面增加了一些欄位,通常後臺開發人員都是忘記去更新文件也許是因為忙,甚至有些同學懶的更新文件,一般這時候都是通過qq或者其他通訊工具告知客戶端開發人員增加了哪些個欄位,欄位含義是什麼。待時間長了,客戶端開發人員忘記欄位含義或者換了另一個開發人員接手,不知道這個欄位含義是什麼,先去翻看以前聊天記錄,找不到去看介面文件,文件還是1.0版本。。。。我去。。。

總結

綜合以上3點,Target_Action更優,我們公司目前也採用的Target_Action方案,如果有同學有計劃切到Swift語言開發,我建議Protocol方案。事實上沒有哪個方案是萬能的,具體的採用還得結合自己的業務以及開發人員的整體素質,如果你還是拿不定主意,阿里開源了一個模組解耦框架BeeHive(protocol註冊),你就向著大廠靠攏吧。Url註冊方案Demo我就不提供了,因為它的可讀性,維護性以及常規引數傳遞的缺點讓我放棄了它,不過Url註冊方案配合伺服器下發路由能夠很好的解決bug,前提是你們的模組得Native開發一套 H5開發一套(或者RN和Weex)

補 業務模組的劃分

有不少同學知道了元件化,但是不知道如何去劃分業務模組,我大致拿京東App某幾個業務舉例見下圖

133839983-ebf3924226177c0e
icon

圖片每個Module元件化後就是一個單獨的project,也許很多project裡面只有一個ViewController,這也是合理的劃分,比如商品詳情,很多模組(服裝城,京東超市,全球購。。。)會呼叫到商品詳情模組,那把商品詳情模組中的業務強行塞到(服裝城,京東超市,全球購。。。)任何一個project都是不合理的,確認訂單同理。元件化是把業務縱切,具體到某個業務模組中network模組,database模組的劃分是橫切

預告

發現很多同學理解MVC的姿勢不對,導致controller很臃腫難以維護,下一篇我會把自己理解的MVC寫成一個Demo,這個Demo會是一個業務比較龐大的模組(所以時間會長一點。畢竟我白天要忙公司的專案,還有幾個個人專案需要維護)在這個Demo中職責劃分會很清楚,敬請期待哈。


全文完

相關文章