簡述
本文主要講述了在iOS開發過程中,模塊化工程架構的一種組織方式,本文主要講述基于cocoapods來做模塊化的方案,詳細講述了iOS開發怎么進行模塊劃分的內容,主要會在以下方面做闡述:
為什么要做模塊化
模塊設計原則
模塊化開發有哪些優點和缺點
解耦與通信
1.為什么要做模塊化?
我們都知道最基本的代碼設計原則:“Don’t repeat yourself!”,每一個工程都會有自己的架構,即使你是剛入門的開發者,寫幾天代碼也會發現要把一些常用到的重復代碼單獨拿出來放在一個叫common的地方,實現代碼復用。這樣看來每個開發者其實都或多或少的做過架構方面的事情,每個團隊至少有1~2個人在做這樣的事情。
說道app代碼架構,記得Samurai的開發者郭虹宇在群里說過這段精辟的話,引用一下:
一派是說app開發并不需要什么狗P架構,第二派說我們有自己NB的架構,第三派說只要模塊化夠好,每個模塊應該有自己的架構。
這三個觀點的出發點,我覺得也比較好理解,第一種應該是一些個人開發者,個人能力很強,經常一個人很快搞出來一個app,他的映像中不需要弄太多的框框框住自己,但是其實他也是有一套自己的架構的。第二派應該是一些公司或者大公司,有一套NB的架構對于團隊的意義就比較大了,可以保證穩定迭代,保證規范和持久可維護性。第三派應該是BAT這樣的有很多BU的超級公司,或者一些先進的開源開發者們,模塊化能夠更好的實現跨app的代碼和功能的復用, 能夠更好的共享資源,避免重復造輪子。
那么為什么要做模塊化?已經很明顯了,模塊化的代碼框架最屌,不信,看看蘋果的框架怎么做的,你就明白了。
2. 模塊設計原則
既然模塊化最屌,那怎么才能做好project的模塊化拆分呢,哪些代碼應該被放到一個模塊?這里分享一些我的經驗。
越底層的模塊,應該越穩定,越抽象,越具有高復用度。
這一點,目測大家應該比較認同,越是底層的SDK,就應該越穩定,穩定的最直觀表現就是API很久都不用變化,所有的變化因子不要暴露出來,避免傳遞給依賴它的模塊。但是要做到設計一套API很久都不用改變,那么就需要設計的時候能越抽象, 即需要我們抽象總結的能力。
穩定性還有一個特點就是會傳遞,比如 B 模塊依賴了 A 模塊,如果 B 模塊很穩定,但是 A 模塊不穩定,那么B模塊也會變的不穩定了,因此下一個原則:
不要讓穩定的模塊依賴不穩定的模塊, 減少依賴
既然上面說最好不要依賴,但是我發現我的 B 模塊的確依賴了 A 模塊里面不可或缺的代碼怎么辦? 假設依賴的代碼段為 x , 現在來看x的特性, 如果X是一個可能高復用的代碼段,那么無妨把x從 A 模塊里面拿出來,單做成一個模塊 X, 那么 B 模塊依賴 X 模塊就好了;靈一種情況,x是一個方法或函數,而且不太適合單做成一個模塊,所以那就在B模塊里面拷貝一份 x 代碼就ok了,因為這樣可以保證模塊的穩定性和自完備性.
如果上面兩種方法都不太合適,我們會在后面解耦里面講到如何解耦
提升模塊的復用度,自完備性有時候要優于代碼復用
什么是自完備性,就是盡可能的依賴少的模塊來達到代碼可復用。
舉個例子,我有個模塊Utils里面放了大量的category工具方法等,在日常UI產品開發中,依賴這個Utils會很方便,但是我現在要寫一個比較基礎的模塊,應該就要求復用度更高一些,這個時候需要用到Utils里面的幾個方法,那這個時候還適合直接依賴Utils嗎,當然不合適了,這與我們上面的設計原則相悖了啊,因此我們這時候為了這個模塊的自完備性,就可以重新實現下這幾個方法,而不是依賴Utils模塊
每個模塊只做好一件事情,不要讓Common出現
模塊化結構是讓工程結構更清晰,每個模塊都只做一件事情,都有自己的一個命名,這樣這個模塊才能良性發展, 但是這個名字千萬不要再叫Common了,試想下你有沒有做過這樣的事情:“哎呀,這塊代碼放哪都不太合適,放Common吧”, 日久以后,這個Common就變成了毒瘤,大家都依賴它,還一堆不相關的代碼,這個Common模塊就是我們設計原則第一點的反面教材: “非常不穩定,大量依賴,全是耦合,整個模塊無法復用到其他app”, 所以刪掉工程里面的Common吧,再遇到不知道放哪的代碼,就要好好思考模塊的設計,再不行如果具有可復用性就單建一個模塊吧,為什么不可以呢?
按照你架構的層數從上到下依賴,不要出現下層模塊依賴上層模塊的現象
業務模塊之間也盡量不要耦合
3. 模塊化開發有哪些優點和缺點
優點: 1、不只提高了代碼的復用度,還可以實現真正的功能復用,比如同樣的功能模塊如果實現了自完備性,可以在多個app中復用 2、業務隔離,跨團隊開發代碼控制和版本風險控制的實現 3、模塊化對代碼的封裝性、合理性都有一定的要求,提升開發同學的設計能力。
缺點,模塊化當然也有它的缺點: 1、入門門檻較高,新手入門需要的成本也更高 2、工具的使用成本,團隊間和模塊間的配合成本升高,開發效率短期會降低。
但是從長期的影響來說,帶來的好處遠大于壞處的,因此模塊化仍然是最佳的架構選擇。
4. 解耦與通信
我先說說為什么要解耦吧,模塊化并不是說你把工程的代碼拆分成 50 個 pod 或者framework就算完事了,要實現模塊之間真正的解耦才算真正的模塊化,否則如果模塊之間還都是互相調用代碼,循環依賴,那么和原本放文件夾里面沒啥兩樣。那么什么是模塊間的解耦呢?
模塊解耦的目標就是, 在基于模塊設計原則上, 讓模塊之間沒有循環依賴, 讓業務模塊之間解除依賴。
4.1 公共模塊下沉
這塊其實還是講的模塊設計,一個工程的架構可能會分為很多層,然而在開發的過程中,很容易有人不注意讓應該處于較底層的模塊依賴了上層的模塊,這種情況下應該對模塊的設計進行改造實現單向依賴。
比如一個常見的普遍的例子: 一個公共的WebView模塊,里面可能有WebViewController的基類,然后還有JSBridge的服務,如果設計的時候沒有注意,很容易在開發過程中,這個模塊被塞入大量的其他業務代碼,依賴了一大堆業務模塊,因為經常注冊JSBridge服務需要跟業務耦合。
這個時候怎么做呢,首先我們要思考WebView模塊的定位,從更全局的角度思考,每個app的架構應該都需要這樣一個模塊,那么我們完全可以把這個模塊單獨拎出來下沉為基礎模塊,這個時候的解耦就需要你對WebView模塊做出一些設計,添加一些注冊型Api,修改JSBridge的服務為可以通過注冊的方式添加邏輯,這樣來實現與業務解耦,業務完全可以把與自己業務相關的代碼放在自己的模塊里面,然后通過你設計的Api注冊到WebView模塊中。
4.2 面向接口調用
雖然說公共模塊可以通過架構設計來避免耦合業務,但是業務模塊之間還是會有耦合的啊,而且這種情況是最多的,比如頁面跳轉啊,數據傳遞啊,這些情況前面的方法已經不夠用了。那如何解耦不同業務模塊之間的代碼調用呢?
那就是面向接口調用,我們知道只要直接引用代碼,就會有依賴,比如:
123456789
// A 模塊-(void)getSomeDataFromB{B.getSomeData();}// B 模塊-(void)getSomeData{returnself.data;}
那么我們可以實現一個getSomeDataFromB的接口,讓 A 只依賴這個接口,而 B 來實現這個接口,這樣就實現了 A 與 B 的解耦。
12345678910111213141516171819
// 接口@protocolBService-(void)getSomeData;@end// A 模塊, 只依賴接口-(void)getSomeDataFromB{idb=findService(@protocol(BService));b.getSomeData;}// B 模塊,實現BService接口@interfaceB:NSObject-(void)getSomeData{returnself.data;}@end
這樣就可以實現了即滿足了模塊之間調用,也實現了解耦
優點:
1、接口類似代碼,可以非常靈活的定義函數和回調等。
缺點:
1、接口定義文件需要放在一個模塊以供依賴,但是這個模塊不回貢獻代碼,所以還好。
2、使用較為麻煩,每各調用都需要定義一個service,并實現, 對于一些具有普適性規律的場景不太合適,比如頁面統一跳轉
4.3 面向協議調用
面向接口調用的缺點導致并不能滿足所有的需求,也解耦的不夠徹底,那么終極手段就是通過定義一套協議來實現模塊間的通信,協議現成的,那就是URL跳轉協議,基本滿足需要,簡單易上手,基本上現在很多的App架構里面都會有“統一跳轉” 這一套東西的,這個不光是對模塊解耦有幫助,對于統一化運營都是有極好的幫助的,比如app里面的任何頁面,或者任何操作都是通過一個URL來喚起的話,這樣是不是就把各個復雜的業務之間解耦了呢,通信都使用URL.
5. 源碼推薦
說了這么多,也要放點干貨吧,下面給出2個庫的介紹,對你模塊化的進程希望有幫助。
1、JLRoutes是一個URL跳轉協議支持的庫,跟我想要的簡直很契合,強烈推薦。
2、 我自己寫的一個解耦框架AppLord. 簡單介紹一下幾個概念
* Module 是負責管理啟動模塊的工具,可以幫助你把AppDelegate里面一坨初始化的代碼分別放到不同的module里面去
* Service 就是對上面 4.2 中面向接口解耦方式的一種封裝
* Task, 全局的后臺任務管理器,有時候一些不知道放哪的任務執行可以塞進去