組件化是一個不可避免的階段迄沫,個人認(rèn)為也是一個越早越好的階段。特別在一個公司內(nèi)的產(chǎn)品越來越多的時候外傅,顯得尤為重要。
什么是組件化
組件化的字面意思俩檬,是代碼上可復(fù)用的結(jié)果萎胰,而實(shí)際上組件化遠(yuǎn)不止這個層面。
在沒有組件化的時候棚辽,每個項(xiàng)目都是各做各的事情技竟,包括一個組內(nèi)也是存在重復(fù)造輪子的行為,等到需要抽離獨(dú)立模塊的時候屈藐,又發(fā)現(xiàn)耦合過大榔组,花費(fèi)大量人力來解耦或者重構(gòu),甚至不了了之联逻。
新項(xiàng)目的快速成型
立項(xiàng)
---> 確定需求
---> 搭建主工程框架
---> 引入需要的組件
---> 補(bǔ)充沒有的組件
---> 成品
在一定的積累下搓扯,一個公司內(nèi)部的新產(chǎn)品開發(fā),都可以快速的搭建起一個擁有公司基礎(chǔ)業(yè)務(wù)組件的框架包归,專心做差異的業(yè)務(wù)開發(fā)锨推,而無須重新花費(fèi)力氣重新開發(fā)這些基礎(chǔ)功能,例如一些性能監(jiān)控公壤,數(shù)據(jù)上報(bào)换可,測試框架等。
研發(fā)思維于流程的質(zhì)變
組件化并不局限于基礎(chǔ)底層的東西厦幅,業(yè)務(wù)邏輯沾鳄,界面封裝都可以成為一個組件,例如通用下發(fā)配置确憨,相機(jī)相冊界面等洞渔,都可以成為大組件化的一個積累過程。
在這樣的環(huán)境下缚态,研發(fā)的開發(fā)流程會變成下面這樣
獲得需求 ---> 前期分析和設(shè)計(jì)
---> Coding ---> 組件化(如果可以)
---> 完成開發(fā)
區(qū)別在于磁椒,在組件化的大環(huán)境下,大家在開發(fā)前期玫芦,會思考這個需求是否可以成為一個通用方案浆熔,而進(jìn)行一些前期的分析和設(shè)計(jì),可以的話,就會從中找到可以組件化的部分医增,成為一個獨(dú)立模塊慎皱。即使不能組件化,也會對于后續(xù)這個模塊的低耦合方面有所貢獻(xiàn)叶骨,引導(dǎo)大家面向接口編程茫多,保持下層穩(wěn)定性。
潛在的激勵
每個研發(fā)都希望自己的代碼得到認(rèn)可忽刽,組件化可以潛在的推動這個事情天揖。從組內(nèi)組件化,到跨項(xiàng)目通用跪帝,到公司內(nèi)公用今膊,最后發(fā)展成對外開源,是一個不斷鼓勵代碼優(yōu)化和維護(hù)的過程伞剑。
探索之路
早期的開發(fā)可能都會經(jīng)歷過這么幾個階段斑唬,現(xiàn)在或許直接就到了最后的形態(tài),再往后就在于是否堅(jiān)持了黎泣。
以下列出了幾種我經(jīng)歷過的工程形態(tài)恕刘,有些或許現(xiàn)在還是這樣,我們可以順著理一下抒倚,這里都是基于iOS的開發(fā)來討論褐着。
一個工程走天下
在早期,或者現(xiàn)在還有些項(xiàng)目衡便,一個app只有一個主程序献起,所有代碼按照文件夾來劃分洋访,實(shí)際上都在一個target上镣陕,很多開發(fā)會覺得很方便啊,這樣有什么不好呢姻政?
- 容易造成import泛濫
- import沒有顯示來源呆抑,不利于代碼理解和解耦
- 沒有一個很好的尋找特定代碼的方向,容易重復(fù)造輪子
- 文件夾劃分太弱汁展,不利于大家規(guī)劃同類的東西在一起鹊碍,甚至一些通用的基礎(chǔ)方法直接寫在業(yè)務(wù)代碼里
- 比如系統(tǒng)的Foundation,如果我找一個字符串操作相關(guān)食绿,我一下子就知道該去這里找了
- 后期要解耦或者抽離的時候侈咕,工作量很大,如果還有交叉循環(huán)依賴器紧,就更痛苦了
- 代碼沒有得到隔離
- 太容易觸碰到的東西耀销,很容易誘發(fā)不能用就改實(shí)現(xiàn)的行為
- 或者完全基于實(shí)現(xiàn)來使用接口,沒有達(dá)到一個面向接口編程的目的
- owner也沒有動力完善接口铲汪,基于實(shí)現(xiàn)告訴使用者怎么用
- 跨項(xiàng)目復(fù)雜度高
- 代碼復(fù)用全靠copy
- 修改bug容易另外一個項(xiàng)目忘記修改
- 如果實(shí)現(xiàn)得太業(yè)務(wù)化熊尉,另外一邊也用不起來
當(dāng)然也不能一棍子打死所有案例罐柳,有些app就是簡單,沒必要那么復(fù)雜化狰住,但是怎么也會有一些自定義的基礎(chǔ)類封裝吧张吉,統(tǒng)一管理出來是個好習(xí)慣,說不定那天會感激自己的多此一舉的
至于公司級別的app催植,基本上就不會簡單到那個地步了肮蛹,至少我個人是這么覺得的。
多工程模式
多工程模式下查邢,一般有點(diǎn)規(guī)模的一個功能蔗崎,都會獨(dú)立成一個工程,放在主工程下扰藕,成為一個子工程缓苛,主工程依賴這個target即可。
多工程和多文件夾的最大區(qū)別在于
- 工程可以有不一樣的配置邓深,文件夾只是很弱的視覺劃分
- 工程有明確的一個獨(dú)立的姿態(tài)
- 工程一般會有一個Dependency的文件夾未桥,把依賴的文件,庫芥备,子工程放這里冬耿,一目了然
- 哪天需要復(fù)用,我很快了解這些依賴有什么萌壳,解決這些依賴就可以了亦镶,文件夾的話,你需要看完每個文件的
.h
和.m
袱瓮,才能匯總起來一共依賴了些什么 - 方便整個功能移植到其他app缤骨,或者在demo上測試開發(fā),不影響主項(xiàng)目的流程
這里的好處是沒有異議的尺借,但是怎么很好地解決依賴問題是一個探索的過程
0x1 拖拽文件
從早期我們遇到一個子工程绊起,需要一來到另外一個子工程的文件的時候,第一想到的辦法就是把那些文件的.h
拖到這個工程來燎斩,然后發(fā)現(xiàn)這個.h
又依賴了另外的.h
虱歪,直到把所有頭文件都拖進(jìn)來了,總算完事了栅表。
但是突然有一個文件上的改動笋鄙,這個行為可能要重新做一次,這就很恐怖了怪瓶。
0x2 子工程化依賴
既然我們已經(jīng)用工程化的行為來模塊化了每個業(yè)務(wù)或者說每個功能集萧落,為什么不還局限于對某個文件的依賴呢?
你依賴一個業(yè)務(wù)里面的文件,很大概率你就會用到這個業(yè)務(wù)里的其他文件铐尚,既然這樣拨脉,我們就把文件級別的依賴升華到工程級別的依賴。
也就意味著我們不再一個文件一個文件的拖宣增,而是整個子工程拖到自己的工程下面玫膀,那么這個工程就可以訪問整個子工程的公開文件了,何樂而不為呢爹脾?
0x3 統(tǒng)一工程化依賴
但是我們發(fā)現(xiàn)帖旨,依然沒有完全解決這個問題,想象一下灵妨,如果某個工程被其他很多工程都依賴了解阅,而這個工程位置變了,一個基礎(chǔ)模塊工程泌霍,一下子需要放到很多很多工程下货抄,這個操作也是很難受的。
我們想了一下朱转,創(chuàng)建了一個Bridge工程
蟹地,所有工程都依賴Bridge工程
,同時所有工程都是Bridge工程
下的子工程藤为。
乍看之下好像有點(diǎn)死循環(huán)怪与,其實(shí)不然。
-
Bridge工程
的編譯并沒有依賴它的子工程的運(yùn)行缅疟,而是依賴子工程下的腳本分别,但是子工程依賴Bridge工程
的編譯 -
Bridge工程
的編譯會引發(fā)每個子工程腳本,生成自己的頭文件路徑存淫,從而給到使用者 - 主程序同樣不依賴
Bridge工程
耘斩,還是按順序依賴其他真是子工程的編譯
這種情況下,為了方便大家纫雁,還創(chuàng)建了一個工程模板煌往,統(tǒng)一標(biāo)準(zhǔn)創(chuàng)建倾哺,且附帶好所有腳本轧邪,唯一需要做的,就是把自己拖進(jìn)Bridge工程
的依賴當(dāng)中羞海。
0x4 腳本時代
說到底忌愚,Bridge工程
開始還是需要人為拖動,且不利于新人理解
我們回歸到最原始最本質(zhì)的問題上:可以使用目標(biāo).h
文件
清晰了這么一點(diǎn)就好辦了
- 每個工程只需要搭配一個配置文件却邓,聲稱我這里哪些文件是Public的硕糊,這樣就可以了
- 然后我們寫一個腳本,遍歷工程文件夾下所有配置文件,在對應(yīng)的目錄生成這些Public Header的soft-link简十,這樣每個子工程都有自己的一個Public Headers的目錄
- 使用者引用對應(yīng)的路徑即可
(可能很多人覺得這只是一個framework的事情檬某,但是那時候還沒有framework,而且目前一些老項(xiàng)目說不定還在支持iOS7)
腳本能帶來的好處是很大的螟蝙,除了方便了很多以外恢恼,甚至可以做到任何交叉循環(huán)依賴,在每個子工程上可以說用的風(fēng)生水起了胰默。
但是埋下的問題也是很可怕的
- 首先是這套方案是內(nèi)部使用的场斑,并不能推廣出去,連跨組推廣都有一定的復(fù)雜度牵署,更別說公司外了
- 然后這套方便的方案漏隐,在設(shè)計(jì)模式層面上,可以說破壞的很厲害了奴迅,會削弱研發(fā)的設(shè)計(jì)思維
- 使用過程沒有規(guī)范化青责,新人有一定的學(xué)習(xí)和理解成本
Workspace模式
0x5 Pod的到來
Pod的規(guī)范化可以說帶來了一個最終的項(xiàng)目工程形態(tài)(當(dāng)然也有些公司有自己更好的一套方案),主要得益于:
- 業(yè)內(nèi)的一個通用標(biāo)準(zhǔn)和規(guī)范取具,降低相互之間的一個學(xué)習(xí)成本
- 更深一層的代碼隔離爽柒,更好的代碼引入方式,更豐富的配置
- 對私對公的repo機(jī)制者填,插件可自定制化浩村,更少的人為配置依賴操作
- 不同分支倉庫的組件差異化
對于Pod的模式和使用上,這里就沒必要多說了占哟,可以到Cocoapods的官網(wǎng)了解詳細(xì)的內(nèi)容心墅,也可以Google一些插件定制化的東西來滿足特定的需求。
這里先回答一個疑問榨乎,Pod畢竟也不是很晚期的東西了怎燥,為什么早期或者中期不直接開始引入這個模式呢?
一個客觀原因是蜜暑,在當(dāng)時Pod剛起步铐姚,或者說還沒有到現(xiàn)在這么普及,我們還在觀望和不熟悉的階段肛捍,的確是有點(diǎn)躊躇番甩,通俗點(diǎn)說,對于陌生的東西则奥,就是有點(diǎn)慫迟杂。
另外一個是歷史原因,我們雖然看到前面幾個階段有很多不規(guī)范不成熟的地方缀蹄,但是它們在當(dāng)時的確是一個相對較佳的方案峭跳,我們來分析一下為什么:
- 這些舊方式都沒有任何難度膘婶,想到一個優(yōu)化點(diǎn)就可以馬上動手,對原來的代碼框架修改很少蛀醉,可以說非常輕量化
- 0x4模式雖然很大程度上破壞了設(shè)計(jì)模式層面悬襟,但是不得不說在中國互聯(lián)網(wǎng)公司快速開發(fā)迭代的節(jié)奏下,還是獲得很多人的青睞拯刁,畢竟項(xiàng)目的發(fā)版壓力還是有的
- 歷史代碼的解耦難度大古胆,一下子需要抽離出來,不是一件容易的事情筛璧,另外大家對歷史 相對穩(wěn)定 的代碼逸绎,也是睜一只眼閉一只眼的態(tài)度
阻力是什么
從前面的分析看來,不難發(fā)現(xiàn)夭谤,組件化的阻力從來不是技術(shù)上的棺牧,而是在于流程上的。
從技術(shù)層面上分析朗儒,組件化無非就是
-
選定組件化的方式
- Pod的方式對我來說已經(jīng)非常合適了颊乘,不行就加點(diǎn)定制化的東西進(jìn)去
- 如果牛逼一點(diǎn)的公司可以有自己更優(yōu)的方案,但是畢竟維護(hù)一整套東西也是個成本
-
代碼解耦抽離
- 從快速抽離的角度來看醉锄,解耦的做法乏悄,沒有什么是一個中間層解決不了的,如果有恳不,就兩個
- 復(fù)雜的模塊頂多多費(fèi)點(diǎn)時間檩小,缺乏人力的情況下,甚至可以大合集先包含進(jìn)去烟勋,盡早先收攏再優(yōu)化
-
后期維護(hù)
- 只是一個繼續(xù)開發(fā)的過程而已
阻力從來都出現(xiàn)在流程上
組件化的推進(jìn)规求,畢竟不是一個組的事情,也不能局限在一個部門上卵惦。在所有方案都確定好的時候阻肿,你發(fā)現(xiàn)需要跨團(tuán)隊(duì)跨部門跨子公司來溝通的時候,才是真正的阻力沮尿。因?yàn)槊總€部門每個組的情況都不一樣丛塌,他們有著各自的項(xiàng)目壓力和技術(shù)方向,更有著不一樣的想法畜疾。
如何推進(jìn)
推進(jìn)的過程赴邻,我個人覺得是個藝術(shù)活,論外交手腕的重要性(會心一笑)
因?yàn)榧夹g(shù)方案都確定好了之后庸疾,剩下的技術(shù)手段都是苦力活乍楚,真正花時間的可能都是交際手段
- 推銷方案当编,并得到認(rèn)同届慈,保持步調(diào)一致
-
確定前期目標(biāo)徒溪,收攏的目標(biāo)項(xiàng)目有哪些,整理出來的組件模塊有哪些金顿,從而確定開刀的
主客項(xiàng)目
是誰-
主客
是誰這個很關(guān)鍵臊泌,相當(dāng)于確定公共模塊的標(biāo)準(zhǔn)是誰,用誰的代碼揍拆,以后誰來維護(hù)
-
-
資源分配渠概,也可以說是工作模式,最大的阻力環(huán)節(jié)
- 各個組來各自安排嫂拴,這樣會由于各自的項(xiàng)目壓力和人力播揪,把周期拖得很長
- 統(tǒng)一出人來幫忙各個組抽離組件,這樣涉及到權(quán)限的問題筒狠,流程上就有點(diǎn)周折
- 另外還需要有人負(fù)責(zé)打通平臺對接猪狈,使得涉及的平臺支持新的方案
-
效果檢驗(yàn),這個是最重要的環(huán)節(jié)辩恼,不讓其他人看到效果雇庙,這個事情只會是一次徒勞的苦力活
- 最容易看到的效果,就是拿新項(xiàng)目來試水灶伊,對比出成品的時間
- 長期來看的話疆前,就是內(nèi)部開源項(xiàng)目的數(shù)量,組件化的數(shù)量聘萨,甚至對外開源的項(xiàng)目情況
-
持續(xù)維護(hù)竹椒,需求是源源不斷的,肯定需要有人持續(xù)跟進(jìn)這個開發(fā)流程上的問題
- 需要持續(xù)優(yōu)化過程中遇到的問題米辐,需要高保證不出錯
- 提升使用組件化的帶來的副作用而影響的效率
這個過程如果是從上往下推動的話碾牌,顯然比從下往上要容易得多。
最后
組件化顯然是一個不可避免的趨勢儡循,如前面提到的舶吗,我個人認(rèn)為是越早開始越好,對于開發(fā)人員的思維定性來說择膝,一旦長期沒有這個概念誓琼,亂糟糟的代碼是不可避免的,也是會互相傳染而泛濫肴捉。
雖然很多產(chǎn)品現(xiàn)在都是不在乎實(shí)現(xiàn)腹侣,但求無錯不崩潰的態(tài)度,顯示也不是一個可持續(xù)發(fā)展的做法齿穗,一定規(guī)模的公司更是要避免這種短期成品的行為傲隶。
也有一部分公司覺得需要的人力很大,在業(yè)務(wù)重壓之下沒有任何資源來做這件事情窃页。我個人認(rèn)為也是一個比較極端武斷的想法跺株,組件化并不要求一次性到達(dá)一個完美的階段复濒,它和業(yè)務(wù)一樣是一個重復(fù)迭代優(yōu)化的過程,在即使最糟糕的情況下乒省,我也可以把所有明顯的獨(dú)立模塊大集合先抽離出來巧颈,不要求代碼有任何改動,只是單純的抽離袖扛,達(dá)到一個可用的階段即可砸泛,這樣就可以先讓全公司的項(xiàng)目,先用上同一份代碼蛆封,無論是資源的釋放唇礁,還是邏輯上的統(tǒng)一,都是一個非常好的開端惨篱,總之代碼的收攏越早越好垒迂。
一些公司也有自己的SDK組,這些其實(shí)是組件化的前身妒蛇,如果能進(jìn)一步去演進(jìn)和統(tǒng)一操作机断,相信會得到更多的好處。
以上僅代表本人個人想法绣夺,歡迎吐槽和交流