iOS組件化方案(二)
概述
這是iOS組件化方案-總結(jié)的第二篇,在本文中我實(shí)現(xiàn)了Target-Action方案的Demo,并與***篇介紹的protocol方案做出對(duì)比。
如果沒有看過我***篇protocol組件化方案的同學(xué),可以先去下載我那篇文章中提供的Demo,方便理解我本文的詳述以及了解我Demo中實(shí)現(xiàn)的業(yè)務(wù)場(chǎng)景,傳送門iOS組件化方案-總結(jié)的***篇
Target-Action方案
國(guó)際慣例先上Demo(下載主工程就好了哈,如果不能理解可以把所有業(yè)務(wù)模塊都下載下來,Casa也提供了官方Demo,我***篇文章中提供了傳送門)
以下鏈接可到原文查看。
- Target-Action方案主工程
- Target-Action方案商品詳情業(yè)務(wù)Category組件地址
- Target-Action方案商品詳情業(yè)務(wù)模塊地址
- Target-Action方案確認(rèn)訂單Category組件地址
- Target-Action方案確認(rèn)訂單業(yè)務(wù)模塊地址
- Target-Action方案CTMediator地址因Casa沒有把CTMediator做成公有庫,所以我是直接拷貝過來做成我的私有庫了。
實(shí)施
如何把模塊做成私有pods我這里就不介紹了,想知道的可以看我***篇組件化介紹文章。我這里只拿確認(rèn)訂單模塊舉例
確認(rèn)訂單模塊是個(gè)單獨(dú)的project,為了避免其他模塊調(diào)用確認(rèn)訂單模塊需引入整個(gè)模塊,這里又做了一個(gè)確認(rèn)訂單業(yè)務(wù)Category的私有組件如下圖
TAConfirmOrderBusinessCategory即是確認(rèn)訂單模塊對(duì)外提供服務(wù)的入口,我們的業(yè)務(wù)場(chǎng)景是商品詳情模塊立即購買進(jìn)入確認(rèn)訂單模塊,確認(rèn)訂單模塊提交訂單后返回商品詳情模塊,同時(shí)得到通知下單成功,所以上圖中入?yún)⑻峁┝薈onfirmComplete的Block,下圖是TAConfirmOrderBusinessCategory.m中的實(shí)現(xiàn)
- #import "CTMediator+TAConfirmOrder.h"
- @implementation CTMediator (TAConfirmOrder)
- - (UIViewController *)confirmOrderViewControllerWithGoodsId:(NSString *)goodsId goodsName:(NSString *)goodsName ConfirmComplete:(dispatch_block_t)confirmComplete
- {
- NSMutableDictionary *params = [[NSMutableDictionary alloc] init];
- params[@"goodsId"] = goodsId;
- params[@"goodsName"] = goodsName;
- params[@"completeBlock"] = confirmComplete;
- return [self performTarget:@"TAConfirmOrder" action:@"ConfirmOrderViewController" params:params shouldCacheTarget:NO];
- }
- @end
OK,TAConfirmOrderBusinessCategory實(shí)現(xiàn)完了,我們來看下TAConfirmOrder模塊,模塊中定義一個(gè)Target_TAConfirmOrder具體實(shí)現(xiàn)如下圖
- @interface Target_TAConfirmOrder : NSObject
- - (UIViewController *)Action_ConfirmOrderViewController:(NSDictionary *)params;
- @end
- @implementation Target_TAConfirmOrder
- - (UIViewController *)Action_ConfirmOrderViewController:(NSDictionary *)params
- {
- TAConfirmOrderViewController *confirmOrderVC = [[TAConfirmOrderViewController alloc] init];
- confirmOrderVC.goodsId = params[@"goodsId"];
- confirmOrderVC.goodsName = params[@"goodsName"];
- confirmOrderVC.confirmComplete = params[@"completeBlock"];
- return confirmOrderVC;
- }
- @end
既然TAConfirmOrderBusinessCategory和TAConfirmOrder是2個(gè)project,那category是如何調(diào)用到Target_TAConfirmOrder的呢?其實(shí)很簡(jiǎn)單,我想看這篇文章的人大部分都知道,無非就是NSClassFromString ,performSelector這些方法,不知道的可以閱讀源碼
到這里我都沒有貼過架構(gòu)圖或者講過原理,只是貼了一部分代碼和講述如何實(shí)現(xiàn),為什么?其實(shí)組件化原理很簡(jiǎn)單,簡(jiǎn)單到比當(dāng)初學(xué)習(xí)UITableView容易多了,我的Demo即原理,如果還是看不明白可以自行g(shù)oogle一輪或者在評(píng)論區(qū)提問.
Target_Action VS Protocol方案
1.是否需要注冊(cè)?
- Target_Action方案不需要注冊(cè)
- Protocol方案需要在啟動(dòng)的時(shí)候向CRProtocolManager注冊(cè)
Target_Action很好的利用了runtime特性,減少注冊(cè)這一步,不過對(duì)于即將切到Swift的同學(xué)就有點(diǎn)尷尬了。
在上篇提供的Protocol方案Demo中,在向CRProtocolManager注冊(cè)服務(wù)的是實(shí)例對(duì)象而非Class,這樣確實(shí)會(huì)造成內(nèi)存常駐,但是無傷大雅,熟悉runtime的同學(xué)應(yīng)該都知道***次調(diào)用某個(gè)類或?qū)ο蟮姆椒?,?huì)構(gòu)建出類對(duì)象,所以無論你用Class注冊(cè)還是實(shí)例對(duì)象注冊(cè)類對(duì)象都在,拋開類對(duì)象對(duì)于一個(gè)不掛任何property的實(shí)例對(duì)象所占用的內(nèi)存是很小的。當(dāng)然你可能會(huì)問既然都差不多你為什么注冊(cè)實(shí)例而不是注冊(cè)Class,注冊(cè)的ServiceProvider實(shí)例對(duì)象在有些情況下可以記錄一些狀態(tài),當(dāng)然這只是極少數(shù)情況下出現(xiàn)的,你如果真要把ServiceProvider當(dāng)單例對(duì)象用,我還是強(qiáng)烈建議注冊(cè)Class
不過我不認(rèn)為ServiceProvider需要向中間件注冊(cè)有邏輯上的問題,區(qū)別只是可省可不省
2.依賴關(guān)系
Target_Action方案中商品詳情模塊依賴TAConfirmOrderBusinessCategory組件來獲取確認(rèn)訂單模塊的服務(wù)
Protocol方案中商品詳情模塊需要依賴CRConfirmOrderServiceProtocol通過CRProtocolManager組件獲取提供服務(wù)的實(shí)例對(duì)象,同時(shí)確認(rèn)訂單模塊也依賴CRConfirmOrderServiceProtocol來注冊(cè)服務(wù)
乍一看Protocol方案依賴關(guān)系好像對(duì)業(yè)務(wù)產(chǎn)生了侵入因?yàn)檎{(diào)用方和實(shí)現(xiàn)方都同時(shí)依賴了CRConfirmOrderServiceProtocol,其實(shí)CRConfirmOrderServiceProtocol應(yīng)當(dāng)屬于確認(rèn)訂單模塊的一部分,把他獨(dú)立出來只是為了避免調(diào)用方需要直接引用實(shí)現(xiàn)方,這個(gè)依賴在架構(gòu)圖中體現(xiàn)的應(yīng)該是虛線而不是實(shí)線。試想如果Target_Action方案不用runtime,那BusinessCategory也需要直接依賴Target。利用runtime中NSProtocolFromString也可以解決對(duì)CRConfirmOrderServiceProtocol的依賴,只是造成一定量的硬編碼不夠優(yōu)雅。(提一下,雖然runtime在一些特定場(chǎng)景給我們開發(fā)帶來一些意想不到的奇效,但是runtime跳過了編譯器檢查,有時(shí)候排除bug比較艱難,所以還是慎用)
另Protocol方案中商品詳情模塊同時(shí)依賴CRConfirmOrderServiceProtocol和CRProtocolManager而Target_Action方案中商品詳情模塊僅依賴TAConfirmOrderBusinessCategory,依賴關(guān)系如下圖
Protocol方案橫向依賴了2者,Target_Action方案縱向依賴。Target_Action設(shè)計(jì)的更優(yōu)異
3.可讀性、硬編碼
Target_Action在Category中將常規(guī)參數(shù)打包成字典,在Target處再把字典拆包成常規(guī)參數(shù),這造成了一定量的硬編碼,不過在現(xiàn)實(shí)開發(fā)中,一個(gè)模塊一個(gè)模塊提供的category通常是一個(gè)人寫的,所以造成的影響微乎其微,但是給其他閱讀代碼的人帶來一些不便,甚至同一個(gè)人寫Cagetory、Target的時(shí)候也需要在2個(gè)project不停切換查看之前在Target中定義的函數(shù)名
Protocol方案中0硬編碼,可讀性更高。
在這里提一下Url注冊(cè)方案,Url注冊(cè)方案我覺得***的問題是大量的硬編碼,可讀性很差,維護(hù)性也很差,對(duì)文檔的依賴度很高,而且需要有人不停督促文檔的更新。我想很多同學(xué)對(duì)此都深有體會(huì),每個(gè)項(xiàng)目***版的接口文檔相對(duì)比較詳細(xì)、全面,隨著版本迭代更新,某個(gè)接口增加了一些字段,通常后臺(tái)開發(fā)人員都是忘記去更新文檔也許是因?yàn)槊?,甚至有些同學(xué)懶的更新文檔,一般這時(shí)候都是通過qq或者其他通訊工具告知客戶端開發(fā)人員增加了哪些個(gè)字段,字段含義是什么。待時(shí)間長(zhǎng)了,客戶端開發(fā)人員忘記字段含義或者換了另一個(gè)開發(fā)人員接手,不知道這個(gè)字段含義是什么,先去翻看以前聊天記錄,找不到去看接口文檔,文檔還是1.0版本。。。。我去。。。
總結(jié)
綜合以上3點(diǎn),Target_Action更優(yōu),我們公司目前也采用的Target_Action方案,如果有同學(xué)有計(jì)劃切到Swift語言開發(fā),我建議Protocol方案。事實(shí)上沒有哪個(gè)方案是***的,具體的采用還得結(jié)合自己的業(yè)務(wù)以及開發(fā)人員的整體素質(zhì),如果你還是拿不定主意,阿里開源了一個(gè)模塊解耦框架BeeHive(protocol注冊(cè)),你就向著大廠靠攏吧。Url注冊(cè)方案Demo我就不提供了,因?yàn)樗目勺x性,維護(hù)性以及常規(guī)參數(shù)傳遞的缺點(diǎn)讓我放棄了它,不過Url注冊(cè)方案配合服務(wù)器下發(fā)路由能夠很好的解決bug,前提是你們的模塊得Native開發(fā)一套 H5開發(fā)一套(或者RN和Weex)
補(bǔ) 業(yè)務(wù)模塊的劃分
有不少同學(xué)知道了組件化,但是不知道如何去劃分業(yè)務(wù)模塊,我大致拿京東App某幾個(gè)業(yè)務(wù)舉例見下圖
圖片每個(gè)Module組件化后就是一個(gè)單獨(dú)的project,也許很多project里面只有一個(gè)ViewController,這也是合理的劃分,比如商品詳情,很多模塊(服裝城,京東超市,全球購。。。)會(huì)調(diào)用到商品詳情模塊,那把商品詳情模塊中的業(yè)務(wù)強(qiáng)行塞到(服裝城,京東超市,全球購。。。)任何一個(gè)project都是不合理的,確認(rèn)訂單同理。組件化是把業(yè)務(wù)縱切,具體到某個(gè)業(yè)務(wù)模塊中network模塊,database模塊的劃分是橫切
預(yù)告
發(fā)現(xiàn)很多同學(xué)理解MVC的姿勢(shì)不對(duì),導(dǎo)致controller很臃腫難以維護(hù),下一篇我會(huì)把自己理解的MVC寫成一個(gè)Demo,這個(gè)Demo會(huì)是一個(gè)業(yè)務(wù)比較龐大的模塊(所以時(shí)間會(huì)長(zhǎng)一點(diǎn)。畢竟我白天要忙公司的項(xiàng)目,還有幾個(gè)個(gè)人項(xiàng)目需要維護(hù))在這個(gè)Demo中職責(zé)劃分會(huì)很清楚,敬請(qǐng)期待哈。