你見過的“垃圾”項目是這樣子么?
本文轉(zhuǎn)載自微信公眾號「跨界架構(gòu)師」,作者Zachary。轉(zhuǎn)載本文請聯(lián)系跨界架構(gòu)師公眾號。
大家好,我是Z哥。
我相信每一位程序員最怕遇到代碼質(zhì)量堪憂的項目,畢竟增加一個同樣的功能,在一個代碼整潔、清晰的項目里與在一個代碼混亂不堪的項目里,效率和質(zhì)量上的差距達(dá)到一個數(shù)量級一點也不奇怪。
但是殘酷的現(xiàn)實是,“垃圾項目”到處存在,哪怕是大廠里也不例外。畢竟,沒有人天生就能寫出無比優(yōu)雅的代碼,都是通過寫“垃圾代碼”一步一步成長起來的,唯一的區(qū)別可能就是成長的速度不同罷了。
一個項目如果運氣好,未來成為受重視的項目,那么它還有機會擺脫不少的“垃圾代碼”,但是如果沒那么好運,那就苦了后來人了。
所以,如果你是一位新人程序員,那么我得提醒你,一定要有一個合理的預(yù)期,這個合理的預(yù)期就是——與垃圾代碼長期為伴。因此,如果你以后因為遇到“垃圾代碼”而破口大罵,這是不講“碼德”的。什么?那么索性跳槽?你怎么能確保下一個接手的項目會更好?
在Z哥看來,一個項目走向“垃圾”、人見人棄的地步,原因只有一個,代碼垃圾,而什么樣的代碼才算垃圾代碼?這就多了,我與你分享一些常見的。
01 組件顆粒度過大
現(xiàn)在三層架構(gòu)基本已經(jīng)成為一個基礎(chǔ)共識了,只要不是太古老的項目,不太會存在UI、業(yè)務(wù)邏輯、數(shù)據(jù)操作寫在一起的情況。(雖然架構(gòu)設(shè)計方面的后起之秀很多,但是三層架構(gòu)因為更容易理解,所以市場占有率依舊最高)
但是三層架構(gòu)的每一層又該如何進一步細(xì)分,如何建模?還沒有能夠成為共識的統(tǒng)一標(biāo)準(zhǔn)或者最佳實踐出來。
所以,很多項目在經(jīng)歷了一年以上的業(yè)務(wù)發(fā)展和變更后,業(yè)務(wù)邏輯層往往率先淪陷,開始出現(xiàn)大對象,成為開發(fā)效率的瓶頸。
近幾年的火熱的領(lǐng)域驅(qū)動設(shè)計其實已經(jīng)開始深入到每一層的內(nèi)部該如何劃分上了。但是離達(dá)到共識還有很長的路要走。
02 API數(shù)量泛濫
有一些經(jīng)驗的程序員們會用一招來讓自己在垃圾代碼中游走自如。這招就是盡量通過“增加”的方式實現(xiàn)功能而不是“修改”歷史代碼。
這種方式的確能避開很多“屎坑”,但是從長期來看,這大概率是在創(chuàng)造新的“屎坑”。
某些核心class里幾百個function,成千上萬行的代碼就是這么來的。
03 低內(nèi)聚、高耦合
「高內(nèi)聚、低耦合」是軟件開發(fā)領(lǐng)域的一條黃金法則,相信每一位程序員都知道。但是現(xiàn)實是,很多項目都存在低內(nèi)聚、高耦合的問題。
出現(xiàn)這個問題倒也不是說是由于大量的重復(fù)代碼導(dǎo)致,而是過度的追求消除重復(fù)代碼。盲目的追求減少重復(fù)代碼,自然會導(dǎo)致代碼被過度拆分,原本一個完整的業(yè)務(wù)處理清清楚楚,硬生生被拆分到多個function里去。
復(fù)用當(dāng)然是好的,但應(yīng)該有個前提條件:不增加系統(tǒng)復(fù)雜度的情況下的復(fù)用。如果不這樣做,損害了穩(wěn)定性,增加了復(fù)雜性,還會造成代碼可讀性降低。
04 業(yè)務(wù)邏輯和程序處理邏輯糾纏
Z哥認(rèn)為,代碼的作用其實就分為兩種,業(yè)務(wù)邏輯和程序處理邏輯。前者體現(xiàn)的是這個軟件的現(xiàn)實價值,后者則是讓軟件能在計算機上run起來必不可少的部分。
程序處理邏輯主要就是各種數(shù)據(jù)的存取、dao操作、序列化/反序列化、緩存操作,以及異常處理、參數(shù)和返回值準(zhǔn)備等等。
如果程序的處理邏輯與業(yè)務(wù)邏輯混在一起,就好比芝士拌飯,都黏在一起,隨便改一小點功能就扯出一大段代碼要看。
更加可怕的是,如果里面還充斥著大量Magic Number,比如if (errCode == 50001) 。那你得忍住不要罵人。
05 又長又寬的if else
這點其實長期以來就是一直被人詬病的陋習(xí)。但是這又幾乎是每一門編程語言都有的語法,那么問題到底在哪里?
因為不管是if else也好,還是switch case也好,其實是一種硬編碼的方式。這就會導(dǎo)致它不容易被修改。
想象一個場景:某個枚舉增加了一個新的值,這個時候你需要將原先判斷這個枚舉的所有if else都檢查一遍,以確保新增的業(yè)務(wù)能正確執(zhí)行。真正做過這件事的人相信有過深刻地感受,如果這個枚舉被用到幾十上百的地方,檢查一遍外加弄懂上下文,估計至少半天時間就沒了,而且很有可能最終還是漏改或者改錯什么。
以上就是一些常見的垃圾代碼的樣子。那么,在你的工作中如果遇到充滿垃圾代碼的垃圾項目該怎么辦呢?從流派上說,主要有三大流派,激進派、保守派、重頭再來派。
保守派的做法,主要是從局部修改問題代碼入手。比如前面提到的這些“垃圾”代碼,常見的處理思路是以下這些。
01 業(yè)務(wù)層禁止層內(nèi)依賴
你可以留意觀察一下,業(yè)務(wù)層內(nèi)的內(nèi)部相互依賴,是導(dǎo)致代碼高耦合低內(nèi)聚的主要推手。因為這會導(dǎo)致一個業(yè)務(wù)方法體內(nèi)部經(jīng)常容易囊括一些不在當(dāng)前業(yè)務(wù)邏輯內(nèi)的分支邏輯,使得每個方法的耦合逐漸提高。
所以,禁止層內(nèi)依賴對降低耦合有立竿見影的效果,這也是領(lǐng)域驅(qū)動設(shè)計提倡專門拉出一個Application Layer來協(xié)調(diào)各個Domain之間的交互的目的。
02 將大函數(shù)拆小
讓代碼恢復(fù)健康,最難啃的骨頭就是那些動輒幾百上千行的函數(shù)。保守派雖然避免了大量應(yīng)用新框架進行替換所帶來的工作量。但是這個最難啃的骨頭是避不開的坎,否則所謂的代碼重構(gòu)只是隔靴搔癢而已,并沒有多大效果。
03 減少代碼
減少代碼最高效的方式是調(diào)用新的系統(tǒng)api或者使用新的框架或者工具。他們可以大批量的代替原先冗余的「程序處理邏輯」。只是對保守派來說,他們會更謹(jǐn)慎地使用新的框架。畢竟,運用一個新框架可是一個不小的手術(shù)。
激進派與保守派最大的區(qū)別在于,他們更傾向于使用新的框架、工具甚至是新的技術(shù)棧、編程語言來解決問題。不過在我看來,這些新東西,可能在短期的確能解決眼前的問題,但是這些問題到底是新技術(shù)解決的?還是因為使用新技術(shù)而大量重寫“新鮮”的代碼解決的?值得打上一個問號。
最后的重頭再來派很有意思。他們認(rèn)為老項目已經(jīng)沒救了,必須重做才行,這是比激進派更激進的一個流派。
可是我見過好多重頭再來派的人員,重頭再來做出的項目,在其他人眼里可能并沒有比之前好多少,甚至在過了一段時間之后,發(fā)現(xiàn)重新陷入了之前的困境之中。
因為盲目樂觀是我們?nèi)诵缘娜觞c。這就像創(chuàng)業(yè)者總覺得自己是九死一生中唯一生的那個,買彩票的人總覺得自己是那個千萬分之一的幸運兒一樣。
這三個流派,并沒有絕對的好壞之分,不同的流派都有不同的適用場景,區(qū)別就在于「時間、成本、質(zhì)量」這三個要素上的取舍不同。所以,我們在選擇方案的時候可以根據(jù)這三個要素的具體情況來判斷。
因為Z哥認(rèn)為一個項目的核心要素就是這三者,并且這三者無法兼得,最多只能取其二,類似于分布式系統(tǒng)領(lǐng)域的CAP定理。
Z哥有幸分別參與和主導(dǎo)過這三個流派的工作,以我的經(jīng)驗來看,我認(rèn)為這三個流派分別適用的項目情況如下:
- 保守派。時間快,成本低,對質(zhì)量僅有最基本的要求。
- 激進派。有較高的質(zhì)量要求,滿足時間較寬?;蛘呓邮艹杀敬蠓黾悠渲械囊稽c。
- 推倒重來派。時間和成本完全給質(zhì)量讓步,追求可見范圍內(nèi)的最好。
其實這么一分析你也能發(fā)現(xiàn),總是推倒重來是不現(xiàn)實的,時間和成本這種屬于資源,資源總是有限的。所以,大多數(shù)情況下,保守派的選擇才是最常見的。因此我們更應(yīng)該思考的是如何通過保守改造最大化提高項目的質(zhì)量,讓他擺脫“垃圾”的標(biāo)簽。這需要我們沉下心來才能完成,不要老想著推翻重做。
好了,總結(jié)一下。
這篇呢,Z哥和你分享了我對如何應(yīng)對“垃圾項目”的看法。
首先和你分析了5個常見的使得項目逐漸變得垃圾的陋習(xí)。
- 組件顆粒度過大
- API數(shù)量泛濫
- 低內(nèi)聚高耦合
- 業(yè)務(wù)邏輯和程序處理邏輯糾纏
- 又長又寬的if else
然后分享了三個常見的保守派做法:
- 業(yè)務(wù)層禁止層內(nèi)依賴
- 將大函數(shù)拆小
- 減少代碼
最后分享了應(yīng)對“垃圾項目”時的三大流派,分別是:
- 保守派
- 激進派
- 推倒重來派
從時間、成本、質(zhì)量三個維度的權(quán)衡來看。保守派才是主流,所以不要動不動就想著推倒重來,收起浮躁的心,沉下心來。
好了,希望對你有所啟發(fā)。