** 注意:本文并非原創(chuàng)文章缤骨,轉(zhuǎn)載自(原創(chuàng)):https://blog.cnbluebox.com/blog/2015/11/28/module-and-decoupling/?hmsr=toutiao.io&utm_medium=toutiao.io&utm_source=toutiao.io **
** 前些天讀到這篇文章上鞠,印象中感覺這篇文章簡短精煉,所以今天重新翻出來分享給大家顶岸! **
簡述
本文主要講述了在iOS開發(fā)過程中腔彰,模塊化工程架構(gòu)的一種組織方式叫编,本文主要講述基于cocoapods來做模塊化的方案,詳細(xì)講述了iOS開發(fā)怎么進(jìn)行模塊劃分的內(nèi)容霹抛,主要會在以下方面做闡述:
- 為什么要做模塊化
- 模塊設(shè)計(jì)原則
- 模塊化開發(fā)有哪些優(yōu)點(diǎn)和缺點(diǎn)
- 解耦與通信
1.為什么要做模塊化搓逾?
我們都知道最基本的代碼設(shè)計(jì)原則:“Don’t repeat yourself!”,每一個工程都會有自己的架構(gòu)杯拐,即使你是剛?cè)腴T的開發(fā)者霞篡,寫幾天代碼也會發(fā)現(xiàn)要把一些常用到的重復(fù)代碼單獨(dú)拿出來放在一個叫common的地方,實(shí)現(xiàn)代碼復(fù)用端逼。這樣看來每個開發(fā)者其實(shí)都或多或少的做過架構(gòu)方面的事情朗兵,每個團(tuán)隊(duì)至少有1~2個人在做這樣的事情。
說到app代碼架構(gòu)顶滩,記得Samurai
的開發(fā)者郭虹宇在群里說過這段精辟的話余掖,引用一下:
一派是說app開發(fā)并不需要什么狗P架構(gòu),第二派說我們有自己NB的架構(gòu)礁鲁,第三派說只要模塊化夠好盐欺,每個模塊應(yīng)該有自己的架構(gòu)。
這三個觀點(diǎn)的出發(fā)點(diǎn)仅醇,我覺得也比較好理解冗美,第一種應(yīng)該是一些個人開發(fā)者,個人能力很強(qiáng)析二,經(jīng)常一個人很快搞出來一個app粉洼,他的映像中不需要弄太多的框框框住自己,但是其實(shí)他也是有一套自己的架構(gòu)的甲抖。第二派應(yīng)該是一些公司或者大公司漆改,有一套NB的架構(gòu)對于團(tuán)隊(duì)的意義就比較大了,可以保證穩(wěn)定迭代准谚,保證規(guī)范和持久可維護(hù)性挫剑。第三派應(yīng)該是BAT這樣的有很多BU的超級公司,或者一些先進(jìn)的開源開發(fā)者們柱衔,模塊化能夠更好的實(shí)現(xiàn)跨app的代碼和功能的復(fù)用, 能夠更好的共享資源樊破,避免重復(fù)造輪子。
那么為什么要做模塊化唆铐?已經(jīng)很明顯了哲戚,模塊化的代碼框架最屌,不信艾岂,看看蘋果的框架怎么做的顺少,你就明白了。
2. 模塊設(shè)計(jì)原則
既然模塊化最屌,那怎么才能做好project的模塊化拆分呢脆炎,哪些代碼應(yīng)該被放到一個模塊梅猿?這里分享一些我的經(jīng)驗(yàn)。
*** 越底層的模塊秒裕,應(yīng)該越穩(wěn)定袱蚓,越抽象,越具有高復(fù)用度几蜻。 ***
這一點(diǎn)喇潘,目測大家應(yīng)該比較認(rèn)同,越是底層的SDK梭稚,就應(yīng)該越穩(wěn)定颖低,穩(wěn)定的最直觀表現(xiàn)就是API很久都不用變化,所有的變化因子不要暴露出來弧烤,避免傳遞給依賴它的模塊枫甲。但是要做到設(shè)計(jì)一套API很久都不用改變,那么就需要設(shè)計(jì)的時候能越抽象, 即需要我們抽象總結(jié)的能力扼褪。
穩(wěn)定性 還有一個特點(diǎn)就是會傳遞,比如 B 模塊依賴了 A 模塊粱栖,如果 B 模塊很穩(wěn)定话浇,但是 A 模塊不穩(wěn)定,那么B模塊也會變的不穩(wěn)定了闹究,因此下一個原則:
*** 不要讓穩(wěn)定的模塊依賴不穩(wěn)定的模塊幔崖, 減少依賴。 ***
既然上面說最好不要依賴渣淤,但是我發(fā)現(xiàn)我的 B 模塊的確依賴了 A 模塊里面不可或缺的代碼怎么辦赏寇? 假設(shè)依賴的代碼段為 x , 現(xiàn)在來看x的特性, 如果X是一個可能高復(fù)用的代碼段,那么無妨把x從 A 模塊里面拿出來价认,單做成一個模塊 X, 那么 B 模塊依賴 X 模塊就好了嗅定;靈一種情況,x是一個方法或函數(shù)用踩,而且不太適合單做成一個模塊渠退,所以那就在B模塊里面拷貝一份 x 代碼就ok了,因?yàn)檫@樣可以保證模塊的 穩(wěn)定性 和 自完備性脐彩。
如果上面兩種方法都不太合適碎乃,我們會在后面解耦里面講到如何解耦。
*** 提升模塊的復(fù)用度惠奸,自完備性有時候要優(yōu)于代碼復(fù)用梅誓。 ***
什么是自完備性,就是盡可能的依賴少的模塊來達(dá)到代碼可復(fù)用。
舉個例子梗掰,我有個模塊 Utils 里面放了大量的category工具方法等嵌言,在日常UI產(chǎn)品開發(fā)中,依賴這個Utils會很方便愧怜,但是我現(xiàn)在要寫一個比較基礎(chǔ)的模塊呀页,應(yīng)該就要求復(fù)用度更高一些,這個時候需要用到Utils里面的幾個方法拥坛,那這個時候還適合直接依賴Utils嗎蓬蝶,當(dāng)然不合適了,這與我們上面的設(shè)計(jì)原則相悖了啊猜惋,因此我們這時候?yàn)榱诉@個模塊的自完備性丸氛,就可以重新實(shí)現(xiàn)下這幾個方法,而不是依賴Utils模塊著摔。
*** 每個模塊只做好一件事情缓窜,不要讓Common出現(xiàn)。 ***
模塊化結(jié)構(gòu)是讓工程結(jié)構(gòu)更清晰谍咆,每個模塊都只做一件事情禾锤,都有自己的一個命名,這樣這個模塊才能良性發(fā)展摹察, 但是這個名字千萬不要再叫Common了恩掷,試想下你有沒有做過這樣的事情:“哎呀,這塊代碼放哪都不太合適供嚎,放Common吧”黄娘, 日久以后,這個Common就變成了毒瘤克滴,大家都依賴它逼争,還一堆不相關(guān)的代碼,這個Common模塊就是我們設(shè)計(jì)原則第一點(diǎn)的反面教材: “非常不穩(wěn)定劝赔,大量依賴誓焦,全是耦合,整個模塊無法復(fù)用到其他app”, 所以刪掉工程里面的Common吧着帽,再遇到不知道放哪的代碼罩阵,就要好好思考模塊的設(shè)計(jì),再不行如果具有可復(fù)用性就單建一個模塊吧启摄,為什么不可以呢稿壁?
按照你架構(gòu)的層數(shù)從上到下依賴,不要出現(xiàn)下層模塊依賴上層模塊的現(xiàn)象歉备。
業(yè)務(wù)模塊之間也盡量不要耦合傅是。
3. 模塊化開發(fā)有哪些優(yōu)點(diǎn)和缺點(diǎn)
優(yōu)點(diǎn): 1、不只提高了代碼的復(fù)用度,還可以實(shí)現(xiàn)真正的功能復(fù)用喧笔,比如同樣的功能模塊如果實(shí)現(xiàn)了自完備性帽驯,可以在多個app中復(fù)用 2、業(yè)務(wù)隔離书闸,跨團(tuán)隊(duì)開發(fā)代碼控制和版本風(fēng)險控制的實(shí)現(xiàn) 3尼变、模塊化對代碼的封裝性、合理性都有一定的要求浆劲,提升開發(fā)同學(xué)的設(shè)計(jì)能力嫌术。
缺點(diǎn):模塊化當(dāng)然也有它的缺點(diǎn): 1、入門門檻較高牌借,新手入門需要的成本也更高 2度气、工具的使用成本,團(tuán)隊(duì)間和模塊間的配合成本升高膨报,開發(fā)效率短期會降低磷籍。
但是從長期的影響來說,帶來的好處遠(yuǎn)大于壞處的现柠,因此模塊化仍然是最佳的架構(gòu)選擇院领。
4. 解耦與通信
我先說說為什么要解耦吧,模塊化并不是說你把工程的代碼拆分成 50 個 pod 或者framework就算完事了够吩,要實(shí)現(xiàn)模塊之間真正的解耦才算真正的模塊化栅盲,否則如果模塊之間還都是互相調(diào)用代碼,循環(huán)依賴废恋,那么和原本放文件夾里面沒啥兩樣。那么什么是模塊間的解耦呢扒寄?
*** 模塊解耦的目標(biāo)就是, 在基于模塊設(shè)計(jì)原則上, 讓模塊之間沒有循環(huán)依賴, 讓業(yè)務(wù)模塊之間解除依賴鱼鼓。 ***
4.1 公共模塊下沉
這塊其實(shí)還是講的模塊設(shè)計(jì),一個工程的架構(gòu)可能會分為很多層该编,然而在開發(fā)的過程中迄本,很容易有人不注意讓應(yīng)該處于較底層的模塊依賴了上層的模塊,這種情況下應(yīng)該對模塊的設(shè)計(jì)進(jìn)行改造實(shí)現(xiàn)單向依賴课竣。
比如一個常見的普遍的例子: 一個公共的WebView模塊嘉赎,里面可能有WebViewController的基類,然后還有JSBridge的服務(wù)于樟,如果設(shè)計(jì)的時候沒有注意公条,很容易在開發(fā)過程中,這個模塊被塞入大量的其他業(yè)務(wù)代碼迂曲,依賴了一大堆業(yè)務(wù)模塊靶橱,因?yàn)榻?jīng)常注冊JSBridge服務(wù)需要跟業(yè)務(wù)耦合。
這個時候怎么做呢,首先我們要思考WebView模塊的定位关霸,從更全局的角度思考传黄,每個app的架構(gòu)應(yīng)該都需要這樣一個模塊,那么我們完全可以把這個模塊單獨(dú)拎出來下沉為基礎(chǔ)模塊队寇,這個時候的解耦就需要你對WebView模塊做出一些設(shè)計(jì)膘掰,添加一些注冊型Api,修改JSBridge的服務(wù)為可以通過注冊的方式添加邏輯佳遣,這樣來實(shí)現(xiàn)與業(yè)務(wù)解耦识埋,業(yè)務(wù)完全可以把與自己業(yè)務(wù)相關(guān)的代碼放在自己的模塊里面,然后通過你設(shè)計(jì)的Api注冊到WebView模塊中苍日。
4.2 面向接口調(diào)用
雖然說公共模塊可以通過架構(gòu)設(shè)計(jì)來避免耦合業(yè)務(wù)惭聂,但是業(yè)務(wù)模塊之間還是會有耦合的啊,而且這種情況是最多的相恃,比如頁面跳轉(zhuǎn)啊辜纲,數(shù)據(jù)傳遞啊,這些情況前面的方法已經(jīng)不夠用了拦耐。那如何解耦不同業(yè)務(wù)模塊之間的代碼調(diào)用呢耕腾?
那就是面向接口調(diào)用,我們知道只要直接引用代碼杀糯,就會有依賴扫俺,比如:
// A 模塊
- (void)getSomeDataFromB {
B.getSomeData();
}
// B 模塊
- (void)getSomeData {
return self.data;
}
那么我們可以實(shí)現(xiàn)一個 getSomeDataFromB
的接口,讓 A 只依賴這個接口固翰,而 B 來實(shí)現(xiàn)這個接口狼纬,這樣就實(shí)現(xiàn)了 A 與 B 的解耦。
// 接口
@protocol BService <NSObject>
- (void)getSomeData;
@end
// A 模塊, 只依賴接口
- (void)getSomeDataFromB {
id b = findService(@protocol(BService));
b.getSomeData;
}
// B 模塊骂际,實(shí)現(xiàn)BService接口
@interface B : NSObject <BService>
- (void)getSomeData {
return self.data;
}
這樣就可以實(shí)現(xiàn)了即滿足了模塊之間調(diào)用疗琉,也實(shí)現(xiàn)了解耦。
優(yōu)點(diǎn):
- 1歉铝、接口類似代碼盈简,可以非常靈活的定義函數(shù)和回調(diào)等。
缺點(diǎn):
- 1太示、接口定義文件需要放在一個模塊以供依賴柠贤,但是這個模塊不回貢獻(xiàn)代碼,所以還好类缤。
- 2臼勉、使用較為麻煩,每各調(diào)用都需要定義一個service餐弱,并實(shí)現(xiàn), 對于一些具有普適性規(guī)律的場景不太合適坚俗,比如頁面統(tǒng)一跳轉(zhuǎn)镜盯。
4.3 面向協(xié)議調(diào)用
面向接口調(diào)用的缺點(diǎn)導(dǎo)致并不能滿足所有的需求,也解耦的不夠徹底猖败,那么終極手段就是通過定義一套協(xié)議來實(shí)現(xiàn)模塊間的通信速缆,協(xié)議現(xiàn)成的,那就是URL
跳轉(zhuǎn)協(xié)議恩闻,基本滿足需要艺糜,簡單易上手,基本上現(xiàn)在很多的App架構(gòu)里面都會有“統(tǒng)一跳轉(zhuǎn)” 這一套東西的幢尚,這個不光是對模塊解耦有幫助破停,對于統(tǒng)一化運(yùn)營都是有極好的幫助的,比如app里面的任何頁面尉剩,或者任何操作都是通過一個URL
來喚起的話真慢,這樣是不是就把各個復(fù)雜的業(yè)務(wù)之間解耦了呢,通信都使用URL
理茎。
5. 源碼推薦
說了這么多黑界,也要放點(diǎn)干貨吧,下面給出2個庫的介紹皂林,對你模塊化的進(jìn)程希望有幫助朗鸠。
1、** JLRoutes** 是一個URL跳轉(zhuǎn)協(xié)議支持的庫础倍,跟我想要的簡直很契合烛占,強(qiáng)烈推薦。
2沟启、 我自己寫的一個解耦框架 AppLord. 簡單介紹一下幾個概念忆家。
- Module 是負(fù)責(zé)管理啟動模塊的工具,可以幫助你把AppDelegate里面一坨初始化的代碼分別放到不同的module里面去
- Service 就是對上面 4.2 中面向接口解耦方式的一種封裝
- Task, 全局的后臺任務(wù)管理器德迹,有時候一些不知道放哪的任務(wù)執(zhí)行可以塞進(jìn)去
文/VV木公子(簡書作者)
PS:如非特別說明芽卿,所有文章均為原創(chuàng)作品,著作權(quán)歸作者所有浦辨,轉(zhuǎn)載轉(zhuǎn)載請聯(lián)系作者獲得授權(quán),并注明出處沼沈,所有打賞均歸本人所有流酬!
如果您是iOS開發(fā)者,或者對本篇文章感興趣列另,請關(guān)注本人芽腾,后續(xù)會更新更多相關(guān)文章!敬請期待页衙!