iOS組件化--我的理解

前言

在上一家公司做了幾個月的SDK開發(fā),那就涉及到“組件化”盐茎,當一個公司的業(yè)務和產(chǎn)品越來越多,越來越復雜的時候徙赢,就要考慮組件化了字柠。那今天就聊一聊組件化。

正文

那什么是組件化呢狡赐?

我想也許大多數(shù)都用過CocoaPods包管理工具募谎,我們pod下來的不同的功能庫其實就是一個組件,我的理解組件是指比較小的功能塊阴汇,這些組件不需要多少組件間通信数冬,沒什么依賴,也就不需要做什么其他處理搀庶,面向對象就能搞定拐纱。

一個 APP 有多個模塊,模塊之間會通信哥倔,互相調用秸架。例如微信讀書有 書籍詳情 想法列表 閱讀器 發(fā)現(xiàn)卡片 等等模塊,這些模塊會互相調用咆蒿,例如 書籍詳情要調起閱讀器和想法列表东抹,閱讀器要調起想法列表和書籍詳情,我們一般會push過去沃测。
看起來挺好缭黔,這樣做簡單明了,沒有多余的東西蒂破,項目初期推薦這樣快速開發(fā)馏谨,但到了項目越來越龐大,這種方式會有什么問題呢附迷?顯而易見惧互,每個模塊都離不開其他模塊哎媚,互相依賴粘在一起成為一坨:


component1.png

這樣揉成一坨對測試/編譯/開發(fā)效率/后續(xù)擴展都有一些壞處,那怎么解開這一坨呢喊儡。很簡單拨与,按軟件工程的思路,下意識就會加一個中間層:


component2png.png

那么問題來了:

  • Mediator 怎么去轉發(fā)組件間調用艾猜?
  1. 一個模塊只跟 Mediator 通信截珍,怎么知道另一個模塊提供了什么接口?
  2. 按上圖的畫法箩朴,模塊和 Mediator 間互相依賴岗喉,怎樣破除這個依賴?
    解決方案
    對于前兩個問題炸庞,最直接的反應就是在 Mediator 直接提供接口钱床,調用對應模塊的方法:
Mediator.m  中間層 
.#import "BookDetailComponent.h"
.#import "ReviewComponent.h"
@implementation Mediator
+ (UIViewController *)BookDetailComponent_viewController:(NSString *)bookId {
 return [BookDetailComponent detailViewController:bookId];
}
+ (UIViewController *)ReviewComponent_viewController:(NSString *)bookId reviewType:(NSInteger)type {
 return [ReviewComponent reviewViewController:bookId type:type];
}
@end
//BookDetailComponent 組件
#import "Mediator.h"
#import "WRBookDetailViewController.h"
@implementation BookDetailComponent
+ (UIViewController *)detailViewController:(NSString *)bookId {
 WRBookDetailViewController *detailVC = [[WRBookDetailViewController alloc] initWithBookId:bookId];
 return detailVC;
}
@end
//ReviewComponent 組件
#import "Mediator.h"
#import "WRReviewViewController.h"
@implementation ReviewComponent
+ (UIViewController *)reviewViewController:(NSString *)bookId type:(NSInteger)type {
 UIViewController *reviewVC = [[WRReviewViewController alloc] initWithBookId:bookId type:type];
 return reviewVC;
}
@end

然后在閱讀模塊里:

//WRReadingViewController.m
#import "Mediator.h"
@implementation WRReadingViewController
- (void)gotoDetail:(NSString *)bookId {
 UIViewController *detailVC = [Mediator BookDetailComponent_viewControllerForDetail:bookId];
 [self.navigationController pushViewController:detailVC];

 UIViewController *reviewVC = [Mediator ReviewComponent_viewController:bookId type:1];
 [self.navigationController pushViewController:reviewVC];
}
@end

這就是一開始架構圖的實現(xiàn),看起來顯然這樣做并沒有什么好處埠居,依賴關系并沒有解除查牌,Mediator 依賴了所有模塊,而調用者又依賴 Mediator滥壕,最后還是一坨互相依賴纸颜,跟原來沒有 Mediator 的方案相比除了更麻煩點其他沒區(qū)別。
那怎么辦呢绎橘。
怎樣讓Mediator解除對各個組件的依賴胁孙,同時又能調到各個組件暴露出來的方法?對于OC有一個法寶可以做到称鳞,就是runtime反射調用:

//Mediator.m
@implementation Mediator
+ (UIViewController *)BookDetailComponent_viewController:(NSString *)bookId {
 Class cls = NSClassFromString(@"BookDetailComponent");
 return [cls performSelector:NSSelectorFromString(@"detailViewController:") withObject:@{@"bookId":bookId}];
}
+ (UIViewController *)ReviewComponent_viewController:(NSString *)bookId type:(NSInteger)type {
 Class cls = NSClassFromString(@"ReviewComponent");
 return [cls performSelector:NSSelectorFromString(@"reviewViewController:") withObject:@{@"bookId":bookId, @"type": @(type)}];
}
@end

這下 Mediator 沒有再對各個組件有依賴了涮较,你看已經(jīng)不需要 #import 什么東西了,對應的架構圖就變成:


component3.png

這樣就完全解耦了冈止,但這樣做的問題是:

  • 調用者寫起來很惡心狂票,代碼提示都沒有,每次調用寫一坨熙暴。
  1. runtime方法的參數(shù)個數(shù)和類型限制闺属,導致只能每個接口都統(tǒng)一傳一個 NSDictionary。這個 NSDictionary里的key value是什么不明確周霉,需要找個地方寫文檔說明和查看掂器。
  2. 編譯器層面不依賴其他組件,實際上還是依賴了诗眨,直接在這里調用唉匾,沒有引入調用的組件時就掛了

把它移到Mediator后:

  • 調用者寫起來不惡心,代碼提示也有了匠楚。
  1. 參數(shù)類型和個數(shù)無限制巍膘,由 Mediator 去轉就行了,組件提供的還是一個 NSDictionary 參數(shù)的接口芋簿,但在Mediator 里可以提供任意類型和個數(shù)的參數(shù)峡懈,像上面的例子顯式要求參數(shù) NSString *bookId 和 NSInteger type。
  2. Mediator可以做統(tǒng)一處理与斤,調用某個組件方法時如果某個組件不存在肪康,可以做相應操作,讓調用者與組件間沒有耦合撩穿。

到這里磷支,基本上能解決我們的問題:各組件互不依賴,組件間調用只依賴中間件Mediator食寡,Mediator不依賴其他組件雾狈。接下來就是優(yōu)化這套寫法,有兩個優(yōu)化點:

  • Mediator 每一個方法里都要寫 runtime 方法抵皱,格式是確定的善榛,這是可以抽取出來的。
  1. 每個組件對外方法都要在 Mediator 寫一遍呻畸,組件一多 Mediator 類的長度是恐怖的移盆。

優(yōu)化后就成了 casa 的方案,target-action 對應第一點伤为,target就是class咒循,action就是selector,通過一些規(guī)則簡化動態(tài)調用绞愚。Category 對應第二點剑鞍,每個組件寫一個 Mediator 的 Category,讓 Mediator 不至于太長爽醋。這里有個demo
總結起來就是蚁署,組件通過中間件通信,中間件通過 runtime 接口解耦蚂四,通過 target-action 簡化寫法光戈,通過 category 感官上分離組件接口代碼。

參考:組件化架構漫談

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末遂赠,一起剝皮案震驚了整個濱河市久妆,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌跷睦,老刑警劉巖筷弦,帶你破解...
    沈念sama閱讀 216,470評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡烂琴,警方通過查閱死者的電腦和手機爹殊,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,393評論 3 392
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來奸绷,“玉大人梗夸,你說我怎么就攤上這事『抛恚” “怎么了反症?”我有些...
    開封第一講書人閱讀 162,577評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長畔派。 經(jīng)常有香客問我铅碍,道長,這世上最難降的妖魔是什么线椰? 我笑而不...
    開封第一講書人閱讀 58,176評論 1 292
  • 正文 為了忘掉前任胞谈,我火速辦了婚禮,結果婚禮上士嚎,老公的妹妹穿的比我還像新娘呜魄。我一直安慰自己,他們只是感情好莱衩,可當我...
    茶點故事閱讀 67,189評論 6 388
  • 文/花漫 我一把揭開白布爵嗅。 她就那樣靜靜地躺著,像睡著了一般笨蚁。 火紅的嫁衣襯著肌膚如雪睹晒。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,155評論 1 299
  • 那天括细,我揣著相機與錄音伪很,去河邊找鬼。 笑死奋单,一個胖子當著我的面吹牛锉试,可吹牛的內容都是我干的。 我是一名探鬼主播览濒,決...
    沈念sama閱讀 40,041評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼呆盖,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了贷笛?” 一聲冷哼從身側響起应又,我...
    開封第一講書人閱讀 38,903評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎乏苦,沒想到半個月后株扛,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,319評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,539評論 2 332
  • 正文 我和宋清朗相戀三年洞就,在試婚紗的時候發(fā)現(xiàn)自己被綠了盆繁。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,703評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡奖磁,死狀恐怖改基,靈堂內的尸體忽然破棺而出繁疤,到底是詐尸還是另有隱情咖为,我是刑警寧澤,帶...
    沈念sama閱讀 35,417評論 5 343
  • 正文 年R本政府宣布稠腊,位于F島的核電站躁染,受9級特大地震影響,放射性物質發(fā)生泄漏架忌。R本人自食惡果不足惜吞彤,卻給世界環(huán)境...
    茶點故事閱讀 41,013評論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望叹放。 院中可真熱鬧饰恕,春花似錦、人聲如沸井仰。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,664評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽俱恶。三九已至雹嗦,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間合是,已是汗流浹背了罪。 一陣腳步聲響...
    開封第一講書人閱讀 32,818評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留聪全,地道東北人泊藕。 一個月前我還...
    沈念sama閱讀 47,711評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像难礼,于是被迫代替她去往敵國和親娃圆。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,601評論 2 353

推薦閱讀更多精彩內容