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

程序員必備寶典:手把手教你清理垃圾代碼

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

0. 值得清理么?

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

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

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

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

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

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

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

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

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

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

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

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

1. 建立測(cè)試用例

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Joel Spolsky寫(xiě)過(guò)一篇經(jīng)典的文章,他沒(méi)有掉入到這個(gè)緊張的爭(zhēng)論的陷阱中。

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

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

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

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

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

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

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

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

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

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

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

  1. 刪除你沒(méi)有使用的功能

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

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

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

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.007, 0); 
  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); 
  24.   

 

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  • 移除你沒(méi)有用到的東西(就像上面建議的一樣)。
  • 簡(jiǎn)化必要的概念,避免不必要的概念。
  • 移除不必要的抽象,用實(shí)際的實(shí)現(xiàn)來(lái)替代。
  • 移除不必要的虛擬化,并且簡(jiǎn)化對(duì)象的結(jié)構(gòu)。

    如果一個(gè)設(shè)置曾經(jīng)使用過(guò),那么就避免在用另外的配置來(lái)運(yùn)行這個(gè)模塊。

9. 就這么多了

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


責(zé)任編輯:張葉青 來(lái)源: eoe Android開(kāi)發(fā)者社區(qū)
相關(guān)推薦

2021-09-26 16:08:23

CC++clang_forma

2011-01-06 10:39:25

.NET程序打包

2021-07-14 09:00:00

JavaFX開(kāi)發(fā)應(yīng)用

2011-01-10 14:41:26

2011-05-03 15:59:00

黑盒打印機(jī)

2024-01-26 08:16:48

Exporter開(kāi)源cprobe

2022-01-08 20:04:20

攔截系統(tǒng)調(diào)用

2023-04-26 12:46:43

DockerSpringKubernetes

2022-12-07 08:42:35

2022-07-27 08:16:22

搜索引擎Lucene

2022-03-14 14:47:21

HarmonyOS操作系統(tǒng)鴻蒙

2020-08-12 09:07:53

Python開(kāi)發(fā)爬蟲(chóng)

2011-02-22 13:46:27

微軟SQL.NET

2021-12-28 08:38:26

Linux 中斷喚醒系統(tǒng)Linux 系統(tǒng)

2021-02-26 11:54:38

MyBatis 插件接口

2021-11-24 16:02:57

鴻蒙HarmonyOS應(yīng)用

2010-04-29 09:49:26

代碼提示SQL Server

2021-06-04 05:18:29

ARM程序Gdbserver

2017-07-07 11:01:04

Spark性能調(diào)優(yōu)

2020-08-12 07:41:39

SQL 優(yōu)化語(yǔ)句
點(diǎn)贊
收藏

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