你寫的代碼就是你的犯罪證據(jù)
最近我工作的主要內(nèi)容,是在和別人結(jié)對(duì)編程,以對(duì)一個(gè)大型的遺留系統(tǒng)項(xiàng)目進(jìn)行重構(gòu)。
過程中,我發(fā)現(xiàn)一個(gè)特別有意思的東西,我重構(gòu)了很多的 if 語(yǔ)句。從這些 if 語(yǔ)句里,大抵是映射出了業(yè)務(wù)的變化。于是,我便想寫一篇文章來記錄一下相關(guān)的心得。
你寫的 if 就是你的犯罪證據(jù)
業(yè)務(wù)的復(fù)雜性,導(dǎo)致了架構(gòu)的復(fù)雜性。在這些代碼故事里,發(fā)生得最多的地方就是 if 語(yǔ)句。所以,你可以從大部分的 if 語(yǔ)句里,看到一些代碼上的壞味道。
業(yè)務(wù)條件復(fù)雜
你先寫了一個(gè) if 語(yǔ)句里面只有一個(gè)條件,沒問題。但是后來的人,又加了一個(gè)條件,因?yàn)闃I(yè)務(wù)上確實(shí)需要這么做。于是,后來,又不得加了一個(gè)if 語(yǔ)句,導(dǎo)致了這個(gè)條件變得更加復(fù)雜。
- if(isCondition && isNotASwitchCase && .... && ....) {
- }
所以,完了,這些代碼越來越難以維護(hù)。
于是,我們應(yīng)對(duì)于這類條件判斷,有兩種做法:提取變量和提取方法。當(dāng)你的判斷條件是一個(gè)方法的時(shí)候,你可以想象一下它的架構(gòu)是多么的復(fù)雜。
難以閱讀的字符串判斷
開始的人加了一個(gè)簡(jiǎn)單的條件判斷,因?yàn)楫?dāng)時(shí)真的只有這么一種業(yè)務(wù)場(chǎng)景。你又不能過度設(shè)計(jì),成一個(gè) switch-case。但是,后來又多了好多個(gè)場(chǎng)景。
- if(aCondition =="A") {
- } elseif(bCondition =="B") {
- }
更不要提有人在每個(gè) if 里寫一個(gè): if (myString.toUpperCase.equals(myOtherString.toUpperCase))。
針對(duì)于有限的 if 語(yǔ)句來說,可以轉(zhuǎn)為 switch case(在 IDEA 里只需要 alt + enter 就可以自動(dòng)完成)。
隨著時(shí)間的推移我們的條件越來越復(fù)雜,我們的 if 語(yǔ)句會(huì)越來越復(fù)雜。
多層嵌套 if 語(yǔ)句
隨著 if 條件進(jìn)一步擴(kuò)大化,我們的條件語(yǔ)句就變成了一個(gè)多層嵌套的循環(huán)語(yǔ)句。每多一層嵌套代碼復(fù)雜度就 * 2,它的閱讀難度就越來越大。于是乎:
- if(condition) {
- if(blabla) {
- ...
- }
- }
面對(duì)這一類 if 條件語(yǔ)句,我們能所做的就是:
- 提供方法
- 反轉(zhuǎn) if 語(yǔ)句
諸如于:
- if(!condition) {return}; // 為了演示方便
- if(blabla){...}
又或者是諸如于三元表達(dá)式,不過我討厭難以閱讀的三元表達(dá)式——但是,只是 true 和 false 的情況下,還是相當(dāng)不錯(cuò)的。
復(fù)雜的 if 塊內(nèi)邏輯
當(dāng)業(yè)務(wù)進(jìn)一步復(fù)雜化的時(shí)候,我們的 if 條件里就充斥著各種各樣的邏輯。
- if(conditionA) {
- blablaA;
- blaA(blabla).blabla;
- }
我們的 if 方法隨之變得越來越長(zhǎng),于是嘗試去抽成一個(gè)方法。但是,當(dāng)你又遇到一個(gè)新的場(chǎng)景時(shí),你又加了一個(gè) if 語(yǔ)句。后來,又又加了一個(gè) if 語(yǔ)句。你才發(fā)現(xiàn)說,『咦,不對(duì),這些 If 語(yǔ)句違反了開閉原則』。
于是,你嘗試把代碼重構(gòu)成多態(tài)以替換 if 語(yǔ)句。
你開心的話,還可以轉(zhuǎn)為 Factory + Strategy。
你開心的話,你也可以將它轉(zhuǎn)為 HashMap 。
但是,在你寫下第一個(gè) if 的時(shí)候,你并不知道它會(huì)變成什么樣的。所以,不要提前去把它轉(zhuǎn)為這么復(fù)雜的架構(gòu)。
上帝 if
如果你的業(yè)務(wù)場(chǎng)景真的超級(jí)復(fù)雜,那么你可能會(huì)看到一個(gè)非常長(zhǎng)的 if 代碼。它可能有幾十個(gè)條件,有幾百行到幾千行的規(guī)模。
那么,你可以嘗試使用注冊(cè)表模式+ 注解,通過反射的方式來重構(gòu)你的 if 語(yǔ)句。
重構(gòu)
在你進(jìn)一步修改代碼之前,讓我們來又雙叕明確一下什么叫重構(gòu)
重構(gòu)(Refactoring)就是通過調(diào)整程序代碼改善軟件的質(zhì)量、性能,使其程序的設(shè)計(jì)模式和架構(gòu)更趨合理,提高軟件的擴(kuò)展性和維護(hù)性。
換句話來說,重構(gòu)只是在改善現(xiàn)有的代碼,使其更易于閱讀,換句話來說就是:Clean Code。而當(dāng)我們說整潔的代碼(Clean Code),說的是易于理解、修改和測(cè)試的。易于理解和修改意味著:
- 易于理解整個(gè)系統(tǒng)的架構(gòu)
- 易于理解整個(gè)應(yīng)用程序的執(zhí)行流程
- 易于理解不同對(duì)象如何相互協(xié)作
- 易于理解理解每種方法的作用
- 易于理解每個(gè)表達(dá)式和變量的目的是什么
而易于理解的前提便是能讓每個(gè)團(tuán)隊(duì)成員快速理解。(PS:當(dāng)然了,若是有些人智商不夠或者經(jīng)驗(yàn)不夠,他/她需要去需要去增強(qiáng)這方面的能力)。這便意味著,出于這樣的目的,你不能編寫過于抽象、簡(jiǎn)練的邏輯。而你又不能寫得過于繁瑣,充滿大量地?zé)o用字符。
若是想使代碼易于測(cè)試,則要先使代碼可測(cè)試。而在這沒有測(cè)試之前,我們是難以對(duì)代碼進(jìn)行大規(guī)模重構(gòu)。所以,我們就陷入了一個(gè)死循環(huán),沒有測(cè)試,測(cè)試不了,沒法重構(gòu)。
WHY
等等,那我們?yōu)槭裁匆M(jìn)行重構(gòu)呢?為了 ¥¥¥¥¥¥¥$$$$$$$$$ => 快速發(fā)布軟件。
當(dāng)軟件是一個(gè)產(chǎn)品而不是一個(gè)項(xiàng)目的時(shí)候,我們就需要不斷發(fā)布新功能,以滿足客戶的要求。而為了快速發(fā)布應(yīng)用,我們需要讓每次的改動(dòng)最小,測(cè)試最少,才能實(shí)現(xiàn)快速發(fā)布。基于這樣一個(gè)目標(biāo),我們會(huì)發(fā)現(xiàn)我們的諸多實(shí)踐都是以此為出發(fā)點(diǎn)的。比如說,我們采用插件化、微服務(wù)化、組件化的方式,都是為了將軟件的改動(dòng)變小,這樣一來,就減少了相應(yīng)部分的測(cè)試工作,從某種意義上來說,就加快了軟件發(fā)布的流程,從而更好的實(shí)現(xiàn)業(yè)務(wù)價(jià)值。因此,我們的第一步就是使二進(jìn)制改動(dòng)最小。而要做到二進(jìn)制改動(dòng)最小,那么我們就要做到高內(nèi)聚、低耦合。
因此,不論是在編程還是在設(shè)計(jì)架構(gòu)的時(shí)候,我們都要盡量滿足 SOLID 五項(xiàng)原則中的:
- 單一職責(zé)原則:它規(guī)定一個(gè)類應(yīng)該只有一個(gè)發(fā)生變化的原因。
- 開閉原則:軟件中的對(duì)象應(yīng)該對(duì)于擴(kuò)展是開放的,但是對(duì)于修改是封閉的。
回到問題上
既然,我們都已經(jīng)知道了,如何去重構(gòu),如何用設(shè)計(jì)模式來解決問題。那么,我們會(huì)讓我們的代碼變得更好嗎?不會(huì),因?yàn)樵诹魉€式的生產(chǎn)里,每個(gè)人都能找到合理的理由。
我們?nèi)粘i_發(fā)的模式是:紅-綠-重構(gòu)。而因?yàn)闀r(shí)間的原因,我們少去了重構(gòu)這一步。
上吊繩驅(qū)動(dòng)開發(fā)
在上吊繩(deadline)的驅(qū)動(dòng)下,我寫了一這篇文章。盡管預(yù)先寫好了文章的大綱,但是有很多字是打錯(cuò)的。
而對(duì)于真實(shí)的業(yè)務(wù)開發(fā)來說,要事先設(shè)計(jì)好相關(guān)功能的架構(gòu),意味著你得有充足的時(shí)間。這樣一來說,你在大的方面上才不會(huì)犯錯(cuò)??墒悄兀阏娴挠心敲炊嗟臅r(shí)間可以設(shè)計(jì)嗎?你今天加的班,還好嗎?
代碼所有權(quán)
改動(dòng)了你的代碼,我就要負(fù)責(zé)。所以,我不去修改別人的代碼。
懼怕修改
沒有測(cè)試,難以理解代碼背后的業(yè)務(wù)原因。外加之組織文化,導(dǎo)致的溝通障礙;又或者是大家都很忙,沒人愿意解釋/回顧一下這一塊的代碼。
能力不夠
對(duì),大部分的問題本質(zhì)都是人的問題。
因?yàn)槟阒恍枰聪?IDEA 的快捷鍵,就能完成上面的大部分重構(gòu)工作。當(dāng)然了,需要有技巧的按,而不是像 Monkey 一樣彈鋼琴。
結(jié)論
開心就好。


























