近幾年組件化大家吵的沸沸揚(yáng)揚(yáng)的,它其實(shí)也不是什么黃金圣衣拧烦,穿上立馬讓你的小宇宙提升幾個(gè)檔次忘闻,也不是海皇的三叉戟屎篱,入手就能呼風(fēng)喚雨服赎,它不過(guò)就是一種app的架構(gòu)思路。其實(shí)真的很簡(jiǎn)單交播,如果你的項(xiàng)目從發(fā)布之初就是用組件化重虑,那么在開(kāi)發(fā)的過(guò)程當(dāng)中勢(shì)必會(huì)少很多麻煩,難點(diǎn)其實(shí)是對(duì)我們龐大古老的工程進(jìn)行組件化的改造秦士。
一.下面我們來(lái)談?wù)劷M件化到底是什么
其實(shí)組件化說(shuō)白了就是將一個(gè)單一的工程分解為各個(gè)獨(dú)立的組件缺厉,然后按照某種方式任意組織成為一個(gè)擁有完整業(yè)務(wù)邏輯的工程。
舉個(gè)例子大家就明白了隧土,一輛完整的汽車從來(lái)都不是由一個(gè)工廠將所有的零部件生產(chǎn)完成的提针,而是輪胎廠生產(chǎn)輪胎,發(fā)動(dòng)機(jī)廠生產(chǎn)發(fā)動(dòng)機(jī)曹傀,玻璃廠生產(chǎn)玻璃等等辐脖,然后再組裝成為一輛完整的汽車。我們的各個(gè)組件或者說(shuō)模塊就是汽車的各個(gè)零部件皆愉,我們的整個(gè)工程我們也把它叫做宿主工程就是我們的汽車嗜价,我們按照一定的規(guī)則把他們拼裝起來(lái)就是一個(gè)業(yè)務(wù)邏輯完整的工程艇抠。
二.組件化產(chǎn)生的原因
那么組件化為何應(yīng)運(yùn)而生,其實(shí)在我們的開(kāi)發(fā)過(guò)程中久锥,如果本身項(xiàng)目的規(guī)模不大家淤,業(yè)務(wù)線比較少,人員也比較少瑟由,我們使用一般的單一開(kāi)發(fā)模式就好了絮重。但是隨著我們的項(xiàng)目不斷的迭代更新,業(yè)務(wù)線越來(lái)越多歹苦,發(fā)開(kāi)人員也組件增多青伤,這個(gè)時(shí)候就會(huì)暴露各種各樣的問(wèn)題
※.耦合性嚴(yán)重
※.編譯速度慢
※.測(cè)試不獨(dú)立
※.無(wú)法使用自己擅長(zhǎng)的設(shè)計(jì)模式
......
※.耦合性嚴(yán)重
關(guān)于耦合性嚴(yán)重這點(diǎn)舉個(gè)大家深有體會(huì)的例子,我們對(duì)接手二手甚至N手項(xiàng)目的時(shí)候都是深惡痛絕的暂氯,因?yàn)槲覀儾恢酪郧暗拈_(kāi)發(fā)人員的思路和架構(gòu)潮模,這個(gè)時(shí)候我們往往面臨著三類問(wèn)題
1.代碼的重構(gòu)
2.增加新功能
3.改bug
就拿我們改bug來(lái)說(shuō)亮蛔,我們由于不了解人家的思路痴施,我們把面前的bug改掉了,結(jié)果我們出了更多得bug究流,越改越多辣吃,頭疼至極,因?yàn)槲覀兛赡軐⒀矍癰ug改掉的同時(shí)芬探,其他同樣依賴改動(dòng)地方的代碼卻不適用了神得,這就非常尷尬了。
同樣地對(duì)我們的新功能開(kāi)發(fā)和代碼重構(gòu)也是如此偷仿,我們好不容易將自己的功能模塊搞定的時(shí)候哩簿,由于對(duì)老模塊有依賴,一旦老模塊中存在某些bug酝静,會(huì)導(dǎo)致我們整個(gè)工程都跑不起來(lái)节榜,我們不但測(cè)試不了自己新寫(xiě)的功能模塊,而且我們可能連bug在哪都不是一時(shí)半會(huì)兒就找到的别智,再加上解決的時(shí)間我們將耗費(fèi)大量的時(shí)間和精力宗苍,大大降低了我們的發(fā)開(kāi)效率。
※.編譯速度慢
隨著工程的業(yè)務(wù)線越來(lái)越多薄榛,發(fā)開(kāi)人員不斷增加讳窟,我們的項(xiàng)目越來(lái)越龐大,往往項(xiàng)目編譯少則一兩分鐘多則幾分鐘敞恋,雖然并不影響我們的開(kāi)發(fā)工作丽啡,但是我們使用組件化的開(kāi)發(fā)配合二進(jìn)制化,我們完全可以提高整個(gè)項(xiàng)目的編譯時(shí)間硬猫。
※.測(cè)試不獨(dú)立
從這張圖我們能看到我們?cè)诎l(fā)開(kāi)完畢我們自己的模塊之后补箍,我們需要對(duì)自己的模塊進(jìn)行測(cè)試倚评,但是主工程里面的其他模塊存在一個(gè)bug,導(dǎo)致我們的工程編譯不了,那么我們開(kāi)發(fā)的模塊勢(shì)必也是測(cè)試不了的馏予,真的很尷尬天梧。
※.無(wú)法使用自己擅長(zhǎng)的設(shè)計(jì)模式
這個(gè)我們稍微說(shuō)一下大家應(yīng)該能明白,如果你的公司主要是使用MVVM的架構(gòu)模式進(jìn)行開(kāi)發(fā)的霞丧,而你只會(huì)使用MVC進(jìn)行開(kāi)發(fā)呢岗,是不是很尷尬,當(dāng)然我們可以按照MVC去套用MVVM進(jìn)行開(kāi)發(fā)蛹尝,但是我給大家畫(huà)一張圖大家就明白了
假設(shè)說(shuō)我們的設(shè)計(jì)模式按照模塊劃分的話后豫,我們沒(méi)法使用MVC去套用我們的MVVM,這樣我們除了去學(xué)習(xí)MVVM之外別無(wú)他法突那,可關(guān)鍵是你真的有足夠的時(shí)間在短時(shí)間內(nèi)上手嗎挫酿?
當(dāng)然如果我們按照功能模塊來(lái)劃分的話,我們的MVC倒是可以套用我們的MVVM愕难,但是你能保證不出問(wèn)題嗎早龟?
三.組件化的優(yōu)勢(shì)
那么我們使用組件化之后到底能達(dá)到什么樣的效果呢?
※組件的獨(dú)立
我們終于可以獨(dú)立編寫(xiě)我們的模塊猫缭,獨(dú)立編譯而不用漫長(zhǎng)的等待主工程長(zhǎng)達(dá)數(shù)分鐘的編譯葱弟,我們?cè)僖膊挥脫?dān)心因?yàn)楦鞣N非自己功能模塊中的bug讓我們寸步難行無(wú)法單獨(dú)測(cè)試了。
※資源的重用
我們項(xiàng)目中各種分類猜丹,宏定義芝加,基礎(chǔ)配置這些基礎(chǔ)的代碼,以及我們的輪播器射窒,選項(xiàng)卡等等這些功能性的自定義UI組件再也不用重復(fù)的拖取或者重寫(xiě)藏杖,我們只需要以pod庫(kù)的形式直接導(dǎo)入到工程中就OK啦。
※高效的迭代
當(dāng)我們需要增加或者刪除某些模塊脉顿,我們只需要將對(duì)應(yīng)的路徑刪除掉蝌麸,就可以一次性將整個(gè)模塊增加或者移除又不會(huì)影響宿主工程的正常運(yùn)行,十分高效弊予。
※配合二進(jìn)制祥楣,可大大提高項(xiàng)目的編譯速度
我們把業(yè)務(wù)性、功能性汉柒、基礎(chǔ)性的模塊拆分成組件以后误褪,可以采用靜態(tài)庫(kù)打包,framework庫(kù)的形式二進(jìn)制化組件碾褂,這樣將大大提高我們的編譯速度兽间。
四.組件化應(yīng)該考慮的問(wèn)題
※組件的劃分
我們一般將組件劃分為三類
基礎(chǔ)組件:
包含基本配置(常量,宏)正塌、分類(各種系統(tǒng)類的擴(kuò)展)嘀略、網(wǎng)絡(luò)(AFN恤溶、SDWebImage封裝)、工具(日期時(shí)間處理帜羊、文件處理咒程、設(shè)備信息等)
功能組件:
包含控件(彈幕、輪播器讼育、選項(xiàng)菜單帐姻、圖文菜單等)、功能(斷點(diǎn)續(xù)傳奶段、音頻處理等)
業(yè)務(wù)組件:
業(yè)務(wù)線一(子業(yè)務(wù)線一饥瓷,子業(yè)務(wù)線二.....)
業(yè)務(wù)線二(子業(yè)務(wù)線一,子業(yè)務(wù)線二.....)
※組件層級(jí)之間的關(guān)系
從這張圖我們可以看出來(lái)痹籍,我們?nèi)蠼M件類呢铆,其實(shí)是有層級(jí)關(guān)系的,我們的業(yè)務(wù)組件既要使用我們的基礎(chǔ)組件也要使用我們的功能組件蹲缠,它屬于我們基礎(chǔ)組件和功能組件的上一層棺克,而我們的基礎(chǔ)組件和功能組件屬于同一層級(jí),他們之間是不能互相產(chǎn)生依賴關(guān)系的吼砂。
如果說(shuō)我們的功能組件的彈幕需要使用到基礎(chǔ)組件中的有關(guān)布局View的分類逆航,我們這個(gè)時(shí)候最好的做法并不是將讓我們的功能組件依賴于我們的基礎(chǔ)組件鼎文,這樣的話別人要使用我們的彈幕卻要將我們整個(gè)基礎(chǔ)組件都下載下來(lái)渔肩,那么我們的組件化就失去了原有的意義。我們?cè)谶@里推薦的做法是講我們所需要的那塊代碼直接拷貝到我們的功能組件當(dāng)中去拇惋,這樣做的好處在于我們的功能組件不需要依賴我們的基礎(chǔ)組件周偎。
同樣在我們?nèi)蠼M件類的內(nèi)部,組件之間也不能產(chǎn)生依賴關(guān)系撑帖,好比我們的彈幕不能依賴于我們的播放器蓉坎,總不能別人要使用我們的彈幕還得把播放器給下載下來(lái)把,這樣也是不合理的胡嘿,對(duì)我們業(yè)務(wù)組件也是一樣的蛉艾,我們要增加或者刪除某個(gè)業(yè)務(wù)線,結(jié)果導(dǎo)致其他的業(yè)務(wù)線沒(méi)法正常的使用了衷敌,這都是不可取的勿侯。
但是某些時(shí)候我們確實(shí)需要使用其他組件里面的內(nèi)容,但是他們之間又不能產(chǎn)生依賴關(guān)系缴罗,這個(gè)時(shí)候我們就要使用到組件間的通訊助琐,這個(gè)在后面會(huì)講到組件間如何通訊。
※組件的存在形式
組件內(nèi)部:根據(jù)設(shè)計(jì)模式劃分文件夾結(jié)構(gòu)
組件形式(對(duì)外):每個(gè)組件都是以pod庫(kù)的形式存在
組件測(cè)試:?jiǎn)为?dú)的測(cè)試工程(這里我們可以通過(guò)創(chuàng)建pod模板庫(kù)形式面氓,直接擁有測(cè)試工程)
※我們是以Cocoapods的形式安裝各個(gè)組件的
這張圖我們可以看到兵钮,我們的業(yè)務(wù)組件是可以依賴我們的基礎(chǔ)組件和我們的功能組件的蛆橡,而我們的業(yè)務(wù)組件都是以pod庫(kù)的形式借助我們Cocoapod安裝到我們宿主工程中去,我們的宿主工程面向的都是我們的業(yè)務(wù)組件掘譬。
※組件間的通訊
上面提到了我們同層次間的組件或者是我們?nèi)蠼M件內(nèi)部的組件之間是不能有依賴關(guān)系的泰演,但是確實(shí)有些時(shí)候我們一個(gè)組件內(nèi)部發(fā)生了一些事件想要告訴其他組件,或者需要調(diào)用某些組件的服務(wù)葱轩,這個(gè)時(shí)候我們就需要用到組件之間的通訊粥血。
這里我們來(lái)講講其中的一種方式--中間件,我們用一張示意圖來(lái)描述一下
在這里我們看到我們組件都是通過(guò)中間件來(lái)進(jìn)行交互的酿箭,組件將內(nèi)部發(fā)生的變化告訴給中間件复亏,中間件在通知其他組件。我們組件把各自的服務(wù)給中間件缭嫡,需要對(duì)應(yīng)服務(wù)的組件就會(huì)去找中間件拿缔御,這樣的話我們組件之間不會(huì)產(chǎn)生依賴關(guān)系,同時(shí)又能進(jìn)行通訊妇蛀。
五.分離組件的難點(diǎn)--解耦
一般在組件化的分離各個(gè)組件的時(shí)候耕突,解耦這個(gè)話題我們是回避不了的,但是其實(shí)我們一般會(huì)遇到兩種情況
1.組件里面依賴其他公共功能
2.組件內(nèi)部需要對(duì)接某個(gè)服務(wù)
組件里面依賴其他公共功能
對(duì)于這種情況评架,我們一般最快的方式就是直接copy代碼眷茁,雖然這個(gè)過(guò)程比較惡心,但是好處就是不會(huì)有額外的依賴纵诞,對(duì)于一些不重要的工具方法上祈,我們都可以拷貝到內(nèi)部來(lái)使用。
舉個(gè)例子大家都明白了浙芙,我們使用獲取屏幕尺寸的方法登刺,而這個(gè)方法我們一般寫(xiě)成宏定義放在我們的基礎(chǔ)組件中,我們的業(yè)務(wù)組件中要用到這個(gè)方法沒(méi)這個(gè)時(shí)候我們沒(méi)必要把我們基礎(chǔ)組件也整個(gè)下載下來(lái)嗡呼,我們直接復(fù)制粘貼這短代碼就好了纸俭。
我們也可以把組件依賴的代碼先做成一個(gè)pod庫(kù),然后依賴這個(gè)pod庫(kù)就好了南窗,這樣我們的問(wèn)題就迎刃而解了揍很。
組件內(nèi)部需要對(duì)接某個(gè)服務(wù)
比如我們控件的內(nèi)部涉及到加載網(wǎng)絡(luò)圖片,我們一般會(huì)用到我們的SDWebImage的框架万伤,雖然我們可以在使用遠(yuǎn)程私有索引庫(kù)的時(shí)候添加依賴窒悔,那么我們?cè)谙螺d我們的私有庫(kù)里面組件的時(shí)候我們可以將SDWebImage一并集成到我們的宿主工程中。如果開(kāi)發(fā)過(guò)程中壕翩,公司用得不是SDWebImage不是會(huì)很尷尬嗎蛉迹?
所以我們使用的方式就是使用block或者代理把這部分職責(zé)丟出去,那么我們就可以自用的選擇我們所需要使用的第三方框架或者公司內(nèi)部寫(xiě)的框架放妈,不用再糾結(jié)了北救。