自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

深度解析:清理爛代碼

開發(fā) 后端 前端
爛代碼不一定是問題,只要它們沒有出錯(cuò),沒有人會(huì)對它嗤之以鼻。但不幸的是,它們沒被發(fā)現(xiàn)的概率太小了。錯(cuò)誤會(huì)被發(fā)現(xiàn)。需要新的功能,新系統(tǒng)發(fā)布了?,F(xiàn)在你不得不面對這堆恐怖的代碼,試著去清理它們。這篇文章為這種不幸的情況提供了一些建議。

猜猜看怎么了!你正”繼承“(接收)了一堆混亂的舊代碼。恭喜你!現(xiàn)在都是你的了?;靵y的代碼可能來自任何地方。中間件,網(wǎng)絡(luò),可能來自你自己的公司。

你知道在一個(gè)角落里有一個(gè)家伙,沒有人過去管他在做什么。猜猜看他一直在做什么?辛辛苦苦寫出了代碼,卻是一堆爛代碼。

你還記得這個(gè)模塊是一個(gè)家伙幾年前寫的,在他離開公司之前。這個(gè)模塊已經(jīng)有20個(gè)不同的人加過補(bǔ)丁,進(jìn)行過代碼修復(fù),而且他們也并不理解代碼到底是做了什么。是的,就是這樣的代碼。

或者你從網(wǎng)上下載下的開源的軟件,你知道它非常的可怕,但是它解決了一個(gè)非常專的并且對你來說非常棘手的問題,解決這個(gè)問題你可能要花上幾年。

爛代碼不一定是問題,只要它們沒有出錯(cuò),沒有人會(huì)對它嗤之以鼻。但不幸的是,它們沒被發(fā)現(xiàn)的概率太小了。錯(cuò)誤會(huì)被發(fā)現(xiàn)。需要新的功能,新系統(tǒng)發(fā)布了?,F(xiàn)在你不得不面對這堆恐怖的代碼,試著去清理它們。這篇文章為這種不幸的情況提供了一些建議。

0. 值得清理么?

第一件你需要問問自己的事情就是代碼值得清理么。我不是說當(dāng)問到是否要清理代碼時(shí),你一定要回答是或者一定回答不是。是你對代碼負(fù)有責(zé)任,也是你需要一直面對它們直到最終寫出的代碼是你樂意維護(hù)的,也是你很自豪的放入代碼庫的。

如果你覺得就算代碼看起來很可怕,也不值得浪費(fèi)你本來就很緊張的時(shí)間來修復(fù)它們。所以你僅僅做了最最微小的調(diào)整解救燃眉之急。

換句話說,你也可以將代碼看作自己的,也可以看作是別人的。

兩種情況都有優(yōu)缺點(diǎn)。優(yōu)秀的程序員看到爛代碼時(shí)會(huì)覺得很難受。他們會(huì)拿出火把和叉子并且高呼:“太亂了,太亂了”。這是一種優(yōu)秀的品質(zhì)。

但是清理代碼是一個(gè)繁雜的工作。很容易就低估了時(shí)間。甚至有時(shí)候和從頭開始寫代碼一樣的耗時(shí)。并且短期并沒有帶來任何的短期效應(yīng)。兩個(gè)星期的時(shí)間清理代碼并不會(huì)帶來任何新的功能,但有可能引入一些新的錯(cuò)誤。

另一方面,如果長時(shí)間不清理代碼可能會(huì)帶來災(zāi)難性的毀滅?;靵y是代碼的殺手。

所以,這并不是一個(gè)容易做出的決定。需要考慮一些事情:

● 你期望對這段代碼做多少改變?你是希望僅僅修改這個(gè)小錯(cuò)誤呢,還是這段代碼還要使用多次,所以你希望將它“調(diào)教”的好些,并且加上新的功能。如果僅僅是修復(fù)一個(gè)錯(cuò)誤,那么最好是別打草驚蛇。然而,如果這個(gè)模塊你需要長期折騰的話,那么現(xiàn)在開始花點(diǎn)時(shí)間來清理它吧,之后會(huì)省掉很多煩惱。

● 你需要或者是你想引入上游的更新嗎?它是一個(gè)正在開發(fā)當(dāng)中的開源項(xiàng)目嗎?如果是的話,并且你想做改變的是上游的代碼,那么你不能對代碼有大的改動(dòng)否則當(dāng)你每次pull代碼的時(shí)候都會(huì)經(jīng)歷一場merge的噩夢。所以你需要做一個(gè)友好的團(tuán)隊(duì)合作者,接受這個(gè)錯(cuò)誤,將帶有你修正的代碼補(bǔ)丁發(fā)給代碼的維護(hù)者。

● 要做多少工作?你一天內(nèi)實(shí)際上能清理多少行代碼?我們估計(jì)多于100行,少于1000行,好,我們假設(shè)是1000行。所以如果一個(gè)模塊有30,000行代碼的話,你可能需要一個(gè)月的時(shí)間。你有那么多時(shí)間嗎?值得這么做么?

● 它是你核心的功能嗎?如果這個(gè)模塊只是邊緣的模塊,譬如字體渲染或者圖像渲染,你可能并不在意它是否是亂七八糟的。你可能全盤不要,將來用另外的東西來代替,誰知道呢。如果這段代碼關(guān)乎核心的性能,你需要慎重對待。

● 這段代碼有多糟糕?如果代碼僅僅有一點(diǎn)點(diǎn)糟糕,那么可能你還是可以忍受的。如果它是不可理喻的,令人崩潰的話,那么我們就必須對它下手了。

1. 建立測試用例

要認(rèn)真清理一段代碼意味著花一段時(shí)間來徹底清理它。你可能會(huì)毀壞它們。

如果你有一個(gè)比較好的測試用例,有一定的覆蓋率,你將會(huì)很容易知道什么已經(jīng)損壞了,并且你能夠很快的知道你犯了什么愚蠢的錯(cuò)誤。想要節(jié)省建立測試用例的時(shí)間在整個(gè)的清理代碼的過程中是可笑的。建立測試用例吧。這是你第一件需要做的事情。

單元測試是最好的,但是所有的代碼并不適應(yīng)單元測試。如果單元測試過于繁瑣,就換用集成測試吧。譬如,一個(gè)游戲關(guān)卡中需要一個(gè)人物完成一系列的動(dòng)作和你清理的代碼有關(guān)。

這樣的測試更加耗時(shí),所以不可能在每一次更改之后都測試一次,雖然這是最理想的情況。因?yàn)槟銓⒚恳淮胃淖兌挤诺搅税姹究刂葡到y(tǒng)中,所以情況還不是那么糟糕。所以每一段時(shí)間(比如,五個(gè)更改)就測試一次。當(dāng)你發(fā)現(xiàn)了一個(gè)問題時(shí),你可以通過二進(jìn)制搜尋最近的幾次commit中找到什么地方導(dǎo)致了問題的發(fā)生。

如果你發(fā)現(xiàn)了測試沒有發(fā)現(xiàn)的問題,確保將這個(gè)也加入到測試中,以便將來可以測試它。

2. 使用代碼版本控制系統(tǒng)

還有人需要被告知要使用代碼版本控制系統(tǒng)嗎?我希望沒有。

清理工作是很關(guān)鍵的。你可能要做很多很多小的修改。如果什么地方出錯(cuò)了,你想回顧版本歷史,你可能找到它錯(cuò)在哪。

如果你和我一樣,你可能有時(shí)重構(gòu)(清理愚蠢的類)的時(shí)候會(huì)出錯(cuò),并且后來意識到這并不是個(gè)好的點(diǎn)子,或者這是個(gè)好點(diǎn)子,但是如果先做了什么之后所有的一切會(huì)變得更簡單。所以你想快速的恢復(fù)一切到原狀并且重新開始。

你的公司應(yīng)該已經(jīng)有代碼控制系統(tǒng)了,你可以在不同的分支進(jìn)行修改,在不打擾別人的情況下隨意的commit。

就算情況不是這樣的,你也應(yīng)該使用版本控制。下載Mercurial(或Git),創(chuàng)建新的倉庫,將代碼從你們公司的愚蠢的系統(tǒng)中簽出并放在這里。在庫中commit你的更改。當(dāng)你完成了之后你可以將所有的一切merge到那愚蠢的系統(tǒng)中。

拷貝庫到一個(gè)代碼控制系統(tǒng)中僅僅需要幾分鐘。很值得這么做。如果你不懂Mercurial,花一個(gè)小時(shí)學(xué)習(xí)它。你會(huì)為你這么做感到高興的。如果你愿意的話,花30個(gè)小時(shí)學(xué)習(xí)下Git(我是開玩笑的!并不用這么久?,F(xiàn)在是“nerd”戰(zhàn)斗的時(shí)候了!)

3. 每次僅僅做一個(gè)小小的改動(dòng)

有兩種方法改進(jìn)壞的代碼:革命和改革。革命是用火把一切都燒掉,從新寫一遍。改革是在不破壞的基礎(chǔ)上每次只進(jìn)行一點(diǎn)小小的改變。

這篇文章是關(guān)于改革的方法。我不是說革命的方法從來不是必要的。有時(shí)代碼太糟糕了,需要用革命的方法。但是那些覺得改革的進(jìn)度太慢的人們往往會(huì)鼓勵(lì)改革,然而經(jīng)常沒有意識到問題的復(fù)雜性,并最終并沒有比現(xiàn)存的系統(tǒng)更好。

Joel Spolsky寫過一篇經(jīng)典的文章,他沒有掉入到這個(gè)緊張的爭論的陷阱中。

改革的最好的方法就是一次只做一個(gè)小的改變,測試它,并且commit它。當(dāng)一個(gè)改變很小時(shí),它更容易理解改動(dòng)的后果以及確保改動(dòng)不會(huì)影響現(xiàn)有的功能。如果什么地方出錯(cuò)了,你僅僅需要核查很少的一部分代碼。

如果你開始做更改并且意識到改得很糟糕,那么你恢復(fù)到上一次的commit,不會(huì)損失太多的無用功。如果你過了一段時(shí)間才發(fā)現(xiàn)什么地方有細(xì)微的差錯(cuò),你可以在版本歷史中使用二進(jìn)制搜找到導(dǎo)致問題的更改。

最常見的錯(cuò)誤就是一次進(jìn)行多處更改。譬如,當(dāng)去除不必要的類層次的勢后,你發(fā)現(xiàn)API的方法并不是像你喜歡的使用方法,而你打算重新組織它們。不要這么做!先去除層次結(jié)構(gòu),commit之后再更改API。

聰明的程序員懂得組織,所以他們也不需要太聰明。

試著找一個(gè)途徑,沿著這個(gè)途徑你可以把代碼變成你想要的模樣,每次只有一點(diǎn)點(diǎn)改動(dòng)。譬如,第一步你重命名方法,使之名字更合理。下一步,你可以將成員變量變成方法的參數(shù)。然后將算法變得更清楚些,等等。

如果你開始做更改,并且發(fā)現(xiàn)比你原先設(shè)想的改變要大,不要害怕又退回去,使用更小的更簡單的步驟去完成同樣的事情.。

4. 不要同時(shí)清理代碼和修正代碼

這是(3)的結(jié)果,但是仍然很重要。

這是一個(gè)常見的問題。你開始察看一個(gè)模塊,是因?yàn)槟阆爰尤肽硞€(gè)新功能。然后你發(fā)現(xiàn)這個(gè)代碼相當(dāng)?shù)脑愀?,所以你開始重新組織它并且加入新的功能。

問題在于清理代碼和修正錯(cuò)誤是完全不同的目標(biāo)。當(dāng)你清理的勢后,你想讓代碼看起來更好,而沒有改變它的功能。當(dāng)你修正錯(cuò)誤時(shí), 你想改變功能。如果你同時(shí)清理代碼和改正錯(cuò)誤,很難保證清理不會(huì)改變什么。

先清理代碼,然后再在一個(gè)干凈的基礎(chǔ)上,加入新的功能。

#p#

5. 刪除你沒有使用的功能

清理的時(shí)間正比于代碼的數(shù)量,復(fù)雜性和糟糕的程度。

如果代碼的功能你目前沒有使用,而且在可預(yù)見的將來也不會(huì)使用,那么就刪除它,這會(huì)減少你瀏覽的代碼數(shù),降低復(fù)雜度(刪除不必要的概念和依賴)。你會(huì)清理的更快的,而且最后的結(jié)果會(huì)更簡單。

不要留著代碼僅僅因?yàn)?ldquo;誰知道呢,你可能某一天需要它”。代碼是有代價(jià)的 – 它需要被移植,修正錯(cuò)誤,被閱讀以及被理解。你有更少的代碼,就更好。就算在最不可能的情況下,你需要這個(gè)舊代碼,你也能從代碼庫中找到它。

6. 刪除大部分的注釋

爛代碼很少會(huì)有好的注釋。它們通常是這樣的:

  1. // Pointless:  
  2.    
  3.     // Set x to 3  
  4.    
  5.     x = 3;  
  6.    
  7. // Incomprehensible:  
  8.    
  9.     // Fix for CB (aug)  
  10.    
  11.     pos += vector3(0, -0.0070);  
  12.    
  13. // Sowing fear and doubt:  
  14.    
  15.     // Really we shouldn't be doing this  
  16.    
  17.     t = get_latest_time();  
  18.    
  19. // Downright lying:  
  20.    
  21.     // p cannot be NULL here  
  22.    
  23.     p->set_speed(0.7); 

看看整個(gè)代碼。如果一個(gè)注釋對你來說不再有意義,也對你理解代碼沒什么幫助,那么就刪除它。否則你只會(huì)浪費(fèi)你的腦力去理解一堆對你理解代碼沒幫助的注釋。

同樣的刪除那些已經(jīng)被注釋掉的代碼。如果你還需要它的時(shí)候,它還在你的代碼倉庫中。

甚至如果注釋是正確而且有用的,記住你還可以重構(gòu)你的代碼??赡墚?dāng)你完成重構(gòu)后,這些注釋不再正確了。這個(gè)世界上還沒有一個(gè)單元測試能夠告訴你注釋是否已經(jīng)損壞了。

好代碼需要很少的注釋因?yàn)榇a自己已經(jīng)自說明了而且很容易理解。擁有好名字的變量不需要注釋去解釋它們的用途。函數(shù)如果有好的輸入輸出,沒有特殊情況時(shí)是不需要說明的。簡單的寫得很好的算法在沒有注釋的情況下也是容易理解的。而斷言記錄了條件和預(yù)測。

大部分情況下,最好的做法是刪除所有舊的注釋,專注于讓代碼變得干凈和具有可讀性,然后再在需要的地方添加代碼 – 這些注釋反應(yīng)新的API的用途以及你對代碼的理解。

7. 避免共享的可更改的狀態(tài)

共享的可更改的狀態(tài)是理解代碼的最大阻礙,因?yàn)樗试S隔一段距離的行動(dòng),一段代碼可以改變另一段完全不同的代碼的行為。人們常說多線程是困難的。事實(shí)上,是由于線程共享了可更改的狀態(tài),才導(dǎo)致了問題。如果你能避免它們的話,多線程并不復(fù)雜。

如果你的目標(biāo)是寫高性能的軟件,你應(yīng)該不能避免一切可更改的狀態(tài),但是你的代碼仍然可以從減少它而獲益。為了“大部分功能完善”而努力吧,確保你確切的知道什么狀態(tài)在什么地方改變了,并且知道原因。

共享的可更改的狀態(tài)來自不同的地方:

● 全局變量。最經(jīng)典的例子?,F(xiàn)在每個(gè)人都知道全局變量的壞處。但是要注意(有時(shí)人們會(huì)忘記),全局變量是唯一的會(huì)造成問題的共享的可更改狀態(tài)。全局常量并不糟糕,Sprintf也不糟糕。

● 對象 – 裝有樂趣的大袋子。對象能夠集合很多方法,無疑可以共享很多可變的狀態(tài)(成員)。如果一個(gè)懶惰的程序員需要將一些信息在方法之間傳遞的話,她可以建立一個(gè)新成員,所以可以依照需要來讀它和寫它。這非常像全局變量。多么有意思!當(dāng)一個(gè)對象有越來越多的成員時(shí),問題就越來越嚴(yán)重。

● 巨大的函數(shù)。你可能已經(jīng)聽說它們了。這種神秘的產(chǎn)物棲息在最黑暗的代碼洞穴的最底層。心眼壞的程序員在陰暗的酒吧里談?wù)撍鼈?,他們的理智被他們遇見的代碼摧毀了:“我不停地向下翻向下翻,我不能相信自己的眼睛。居然有12,000行。”當(dāng)函數(shù)足夠長的時(shí)候,它們本地變量將和全局變量一樣糟糕。我們不可能知道改變2000行之后的一個(gè)局部變量會(huì)有什么效果。

● 引用和指針參數(shù)。引用和指針參數(shù)沒有被聲明為const被傳進(jìn)函數(shù)時(shí),可以在被調(diào)用者,調(diào)用者以及任何能被傳遞相同的指針的對象之間充當(dāng)共享的可變的狀態(tài)。

這里有一些避免共享的可更改的狀態(tài)的建議:

  • 將較大的函數(shù)切分成較小的函數(shù)。
  • 將較大的對象切分成較小的變量,將相關(guān)的成員放在一起。
  • 將成員變成private。
  • 將函數(shù)聲明const,返回結(jié)果,而不是可更改的狀態(tài)。
  • 將函數(shù)聲明static,從參數(shù)獲得值,而不是從共享狀態(tài)那里取值。
  • 避免完全使用對象,實(shí)現(xiàn)純凈的功能,不要引入副作用。
  • 將本地變量聲明const。
  • 將指針和引用聲明const。

8. 避免不必要的復(fù)雜性

不必要的復(fù)雜性通常是過度工程化的結(jié)果 – 支持的結(jié)構(gòu)(如序列化,引用計(jì)數(shù)器,虛擬接口,抽象工廠,訪問者等等)會(huì)拖慢真正有實(shí)際功能的代碼。

有時(shí)候過工程化是因?yàn)橐恍╉?xiàng)目開始的時(shí)候有一些更大的野心,多于實(shí)際完成的。更多的情況,我想是因?yàn)槌绦騿T讀了關(guān)于設(shè)計(jì)模式的書之后和瀑布模型之后的想法,他認(rèn)為過工程化會(huì)形成更“堅(jiān)固”和“高質(zhì)量”的產(chǎn)品。

通常,這個(gè)笨重的,僵化的,過度復(fù)雜的模型不能適應(yīng)功能需求,而這是設(shè)計(jì)師不期望的。那些功能可能之后用hack的方式來實(shí)現(xiàn),成了在象牙塔最頂上的螺栓和后門,變成了神經(jīng)錯(cuò)亂的混合結(jié)構(gòu)。

治愈過度工程化的方法就是YAGNI(you are not gonna need it)-你不需要它!只有當(dāng)需要一個(gè)東西的時(shí)候才建造它。當(dāng)你需要它的時(shí)候才建立更復(fù)雜的東西,而不是在你需要之前。

避免不必要的復(fù)雜性的一些實(shí)際的方法:

  • 移除你沒有用到的東西(就像上面建議的一樣)。
  • 簡化必要的概念,避免不必要的概念。
  • 移除不必要的抽象,用實(shí)際的實(shí)現(xiàn)來替代。
  • 移除不必要的虛擬化,并且簡化對象的結(jié)構(gòu)。
  • 如果一個(gè)設(shè)置曾經(jīng)使用過,那么就避免在用另外的配置來運(yùn)行這個(gè)模塊。

9. 就這么多了

現(xiàn)在開始清理你的“房間”吧!

原文鏈接: Niklas Frykholm    翻譯: 伯樂在線 - 唐小娟

譯文鏈接: http://blog.jobbole.com/28672/

責(zé)任編輯:林師授 來源: 伯樂在線
相關(guān)推薦

2016-12-09 15:02:02

云計(jì)算

2015-09-14 09:28:47

2015-08-13 10:54:46

2023-10-07 08:28:06

語言模型代碼技術(shù)

2023-01-16 07:35:40

代碼編碼重構(gòu)

2020-03-12 07:42:49

代碼程序員

2017-04-10 18:10:31

2015-07-23 09:40:24

爛代碼程序員

2015-07-23 09:30:43

爛代碼程序員

2024-01-11 12:14:31

Async線程池任務(wù)

2020-02-10 13:22:35

編程語言機(jī)器學(xué)習(xí)Python

2020-02-24 10:45:44

代碼開發(fā)工具

2023-03-27 08:12:40

源碼場景案例

2023-10-10 11:02:00

LSM Tree數(shù)據(jù)庫

2024-02-21 23:03:56

代碼系統(tǒng)

2020-02-27 16:17:31

代碼規(guī)范codegithub

2013-12-09 10:34:12

2023-03-06 11:13:20

Spring注解加載

2023-03-13 08:12:25

@DependsOn源碼場景

2017-01-15 09:53:49

點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號