北京尚学堂|程序员的智慧
2019獨(dú)角獸企業(yè)重金招聘Python工程師標(biāo)準(zhǔn)>>>
版權(quán)聲明:本文為北京尚學(xué)堂原創(chuàng)文章,未經(jīng)允許不得轉(zhuǎn)載。
?
編程是一種創(chuàng)造性的工作,是一門藝術(shù)。精通任何一門藝術(shù),都需要很多的練習(xí)和領(lǐng)悟,所以這里提出的“智慧”,并不是號稱一天瘦十斤的減肥藥,它并不能代替你自己的勤奮。然而由于軟件行業(yè)喜歡標(biāo)新立異,喜歡把簡單的事情搞復(fù)雜,我希望這些文字能給迷惑中的人們指出一些正確的方向,讓他們少走一些彎路,基本做到一分耕耘一分收獲。
反復(fù)推敲代碼
既然“天才是百分之一的靈感,百分之九十九的汗水”,那我先來談?wù)勥@汗水的部分吧。有人問我,提高編程水平最有效的辦法是什么?我想了很久,終于發(fā)現(xiàn)最有效的辦法,其實是反反復(fù)復(fù)地修改和推敲代碼。
在IU的時候,由于Dan Friedman的嚴(yán)格教導(dǎo),我們以寫出冗長復(fù)雜的代碼為恥。如果你代碼多寫了幾行,這老頑童就會大笑,說:“當(dāng)年我解決這個問題,只寫了5行代碼,你回去再想想吧……” 當(dāng)然,有時候他只是夸張一下,故意刺激你的,其實沒有人能只用5行代碼完成。然而這種提煉代碼,減少冗余的習(xí)慣,卻由此深入了我的骨髓。
有些人喜歡炫耀自己寫了多少多少萬行的代碼,仿佛代碼的數(shù)量是衡量編程水平的標(biāo)準(zhǔn)。然而,如果你總是匆匆寫出代碼,卻從來不回頭去推敲,修改和提煉,其實是不可能提高編程水平的。你會制造出越來越多平庸甚至糟糕的代碼。在這種意義上,很多人所謂的“工作經(jīng)驗”,跟他代碼的質(zhì)量,其實不一定成正比。如果有幾十年的工作經(jīng)驗,卻從來不回頭去提煉和反思自己的代碼,那么他也許還不如一個只有一兩年經(jīng)驗,卻喜歡反復(fù)推敲,仔細(xì)領(lǐng)悟的人。
有位文豪說得好:“看一個作家的水平,不是看他發(fā)表了多少文字,而要看他的廢紙簍里扔掉了多少。” 我覺得同樣的理論適用于編程。好的程序員,他們刪掉的代碼,比留下來的還要多很多。如果你看見一個人寫了很多代碼,卻沒有刪掉多少,那他的代碼一定有很多垃圾。
就像文學(xué)作品一樣,代碼是不可能一蹴而就的。靈感似乎總是零零星星,陸陸續(xù)續(xù)到來的。任何人都不可能一筆呵成,就算再厲害的程序員,也需要經(jīng)過一段時間,才能發(fā)現(xiàn)最簡單優(yōu)雅的寫法。有時候你反復(fù)提煉一段代碼,覺得到了頂峰,沒法再改進(jìn)了,可是過了幾個月再回頭來看,又發(fā)現(xiàn)好多可以改進(jìn)和簡化的地方。這跟寫文章一模一樣,回頭看幾個月或者幾年前寫的東西,你總能發(fā)現(xiàn)一些改進(jìn)。
所以如果反復(fù)提煉代碼已經(jīng)不再有進(jìn)展,那么你可以暫時把它放下。過幾個星期或者幾個月再回頭來看,也許就有煥然一新的靈感。這樣反反復(fù)復(fù)很多次之后,你就積累起了靈感和智慧,從而能夠在遇到新問題的時候直接朝正確,或者接近正確的方向前進(jìn)。
寫優(yōu)雅的代碼
人們都討厭“面條代碼”(spaghetti code),因為它就像面條一樣繞來繞去,沒法理清頭緒。那么優(yōu)雅的代碼一般是什么形狀的呢?經(jīng)過多年的觀察,我發(fā)現(xiàn)優(yōu)雅的代碼,在形狀上有一些明顯的特征。
如果我們忽略具體的內(nèi)容,從大體結(jié)構(gòu)上來看,優(yōu)雅的代碼看起來就像是一些整整齊齊,套在一起的盒子。如果跟整理房間做一個類比,就很容易理解。如果你把所有物品都丟在一個很大的抽屜里,那么它們就會全都混在一起。你就很難整理,很難迅速的找到需要的東西。但是如果你在抽屜里再放幾個小盒子,把物品分門別類放進(jìn)去,那么它們就不會到處亂跑,你就可以比較容易的找到和管理它們。
優(yōu)雅的代碼的另一個特征是,它的邏輯大體上看起來,是枝丫分明的樹狀結(jié)構(gòu)(tree)。這是因為程序所做的幾乎一切事情,都是信息的傳遞和分支。你可以把代碼看成是一個電路,電流經(jīng)過導(dǎo)線,分流或者匯合。如果你是這樣思考的,你的代碼里就會比較少出現(xiàn)只有一個分支的if語句,它看起來就會像這個樣子:
if (...) {
if (...) {
...
} else {
...
}
} else if (...) {
...
} else {
...
}
注意到了嗎?在我的代碼里面,if語句幾乎總是有兩個分支。它們有可能嵌套,有多層的縮進(jìn),而且else分支里面有可能出現(xiàn)少量重復(fù)的代碼。然而這樣的結(jié)構(gòu),邏輯卻非常嚴(yán)密和清晰。在后面我會告訴你為什么if語句最好有兩個分支。
寫模塊化的代碼
有些人吵著鬧著要讓程序“模塊化”,結(jié)果他們的做法是把代碼分部到多個文件和目錄里面,然后把這些目錄或者文件叫做“module”。他們甚至把這些目錄分放在不同的VCS repo里面。結(jié)果這樣的作法并沒有帶來合作的流暢,而是帶來了許多的麻煩。這是因為他們其實并不理解什么叫做“模塊”,膚淺的把代碼切割開來,分放在不同的位置,其實非但不能達(dá)到模塊化的目的,而且制造了不必要的麻煩。
真正的模塊化,并不是文本意義上的,而是邏輯意義上的。一個模塊應(yīng)該像一個電路芯片,它有定義良好的輸入和輸出。實際上一種很好的模塊化方法早已經(jīng)存在,它的名字叫做“函數(shù)”。每一個函數(shù)都有明確的輸入(參數(shù))和輸出(返回值),同一個文件里可以包含多個函數(shù),所以你其實根本不需要把代碼分開在多個文件或者目錄里面,同樣可以完成代碼的模塊化。我可以把代碼全都寫在同一個文件里,卻仍然是非常模塊化的代碼。
想要達(dá)到很好的模塊化,你需要做到以下幾點:
- 避免寫太長的函數(shù)。如果發(fā)現(xiàn)函數(shù)太大了,就應(yīng)該把它拆分成幾個更小的。通常我寫的函數(shù)長度都不超過40行。對比一下,一般筆記本電腦屏幕所能容納的代碼行數(shù)是50行。我可以一目了然的看見一個40行的函數(shù),而不需要滾屏。只有40行而不是50行的原因是,我的眼球不轉(zhuǎn)的話,最大的視角只看得到40行代碼。
如果我看代碼不轉(zhuǎn)眼球的話,我就能把整片代碼完整的映射到我的視覺神經(jīng)里,這樣就算忽然閉上眼睛,我也能看得見這段代碼。我發(fā)現(xiàn)閉上眼睛的時候,大腦能夠更加有效地處理代碼,你能想象這段代碼可以變成什么其它的形狀。40行并不是一個很大的限制,因為函數(shù)里面比較復(fù)雜的部分,往往早就被我提取出去,做成了更小的函數(shù),然后從原來的函數(shù)里面調(diào)用。
- 制造小的工具函數(shù)。如果你仔細(xì)觀察代碼,就會發(fā)現(xiàn)其實里面有很多的重復(fù)。這些常用的代碼,不管它有多短,提取出去做成函數(shù),都可能是會有好處的。有些幫助函數(shù)也許就只有兩行,然而它們卻能大大簡化主要函數(shù)里面的邏輯。
有些人不喜歡使用小的函數(shù),因為他們想避免函數(shù)調(diào)用的開銷,結(jié)果他們寫出幾百行之大的函數(shù)。這是一種過時的觀念。現(xiàn)代的編譯器都能自動的把小的函數(shù)內(nèi)聯(lián)(inline)到調(diào)用它的地方,所以根本不產(chǎn)生函數(shù)調(diào)用,也就不會產(chǎn)生任何多余的開銷。
寫可讀的代碼
有些人以為寫很多注釋就可以讓代碼更加可讀,然而卻發(fā)現(xiàn)事與愿違。注釋不但沒能讓代碼變得可讀,反而由于大量的注釋充斥在代碼中間,讓程序變得障眼難讀。而且代碼的邏輯一旦修改,就會有很多的注釋變得過時,需要更新。修改注釋是相當(dāng)大的負(fù)擔(dān),所以大量的注釋,反而成為了妨礙改進(jìn)代碼的絆腳石。
實際上,真正優(yōu)雅可讀的代碼,是幾乎不需要注釋的。如果你發(fā)現(xiàn)需要寫很多注釋,那么你的代碼肯定是含混晦澀,邏輯不清晰的。其實,程序語言相比自然語言,是更加強(qiáng)大而嚴(yán)謹(jǐn)?shù)?#xff0c;它其實具有自然語言最主要的元素:主語,謂語,賓語,名詞,動詞,如果,那么,否則,是,不是,…… 所以如果你充分利用了程序語言的表達(dá)能力,你完全可以用程序本身來表達(dá)它到底在干什么,而不需要自然語言的輔助。
有少數(shù)的時候,你也許會為了繞過其他一些代碼的設(shè)計問題,采用一些違反直覺的作法。這時候你可以使用很短注釋,說明為什么要寫成那奇怪的樣子。這樣的情況應(yīng)該少出現(xiàn),否則這意味著整個代碼的設(shè)計都有問題。
寫簡單的代碼
程序語言都喜歡標(biāo)新立異,提供這樣那樣的“特性”,然而有些特性其實并不是什么好東西。很多特性都經(jīng)不起時間的考驗,最后帶來的麻煩,比解決的問題還多。很多人盲目的追求“短小”和“精悍”,或者為了顯示自己頭腦聰明,學(xué)得快,所以喜歡利用語言里的一些特殊構(gòu)造,寫出過于“聰明”,難以理解的代碼。
并不是語言提供什么,你就一定要把它用上的。實際上你只需要其中很小的一部分功能,就能寫出優(yōu)秀的代碼。我一向反對“充分利用”程序語言里的所有特性。實際上,我心目中有一套最好的構(gòu)造。不管語言提供了多么“神奇”的,“新”的特性,我基本都只用經(jīng)過千錘百煉,我覺得值得信奈的那一套。
現(xiàn)在針對一些有問題的語言特性,我介紹一些我自己使用的代碼規(guī)范,并且講解一下為什么它們能讓代碼更簡單。
- 避免使用自增減表達(dá)式(i++,++i,i--,--i)。這種自增減操作表達(dá)式其實是歷史遺留的設(shè)計失誤。它們含義蹊蹺,非常容易弄錯。它們把讀和寫這兩種完全不同的操作,混淆纏繞在一起,把語義搞得烏七八糟。含有它們的表達(dá)式,結(jié)果可能取決于求值順序,所以它可能在某種編譯器下能正確運(yùn)行,換一個編譯器就出現(xiàn)離奇的錯誤。
其實這兩個表達(dá)式完全可以分解成兩步,把讀和寫分開:一步更新i的值,另外一步使用i的值。比如,如果你想寫foo(i++),你完全可以把它拆成int t = i; i += 1; foo(t);。如果你想寫foo(++i),可以拆成i += 1; foo(i); 拆開之后的代碼,含義完全一致,卻清晰很多。到底更新是在取值之前還是之后,一目了然。
- 有人也許以為i++或者++i的效率比拆開之后要高,這只是一種錯覺。這些代碼經(jīng)過基本的編譯器優(yōu)化之后,生成的機(jī)器代碼是完全沒有區(qū)別的。自增減表達(dá)式只有在兩種情況下才可以安全的使用。一種是在for循環(huán)的update部分,比如for(int i = 0; i < 5; i++)。另一種情況是寫成單獨(dú)的一行,比如i++;。這兩種情況是完全沒有歧義的。你需要避免其它的情況,比如用在復(fù)雜的表達(dá)式里面,比如foo(i++),foo(++i) + foo(i),…… 沒有人應(yīng)該知道,或者去追究這些是什么意思。
- 永遠(yuǎn)不要省略花括號。很多語言允許你在某種情況下省略掉花括號,比如C,Java都允許你在if語句里面只有一句話的時候省略掉花括號:
if (...)
action1();
- 避免使用continue和break。循環(huán)語句(for,while)里面出現(xiàn)return是沒問題的,然而如果你使用了continue或者break,就會讓循環(huán)的邏輯和終止條件變得復(fù)雜,難以確保正確。
出現(xiàn)continue或者break的原因,往往是對循環(huán)的邏輯沒有想清楚。如果你考慮周全了,應(yīng)該是幾乎不需要continue或者break的。如果你的循環(huán)里出現(xiàn)了continue或者break,你就應(yīng)該考慮改寫這個循環(huán)。
另外一種過度工程的來源,是過度的關(guān)心“代碼重用”。很多人“可用”的代碼還沒寫出來呢,就在關(guān)心“重用”。為了讓代碼可以重用,最后被自己搞出來的各種框架捆住手腳,最后連可用的代碼就沒寫好。如果可用的代碼都寫不好,又何談重用呢?很多一開頭就考慮太多重用的工程,到后來被人完全拋棄,沒人用了,因為別人發(fā)現(xiàn)這些代碼太難懂了,自己從頭開始寫一個,反而省好多事。
過度地關(guān)心“測試”,也會引起過度工程。有些人為了測試,把本來很簡單的代碼改成“方便測試”的形式,結(jié)果引入很多復(fù)雜性,以至于本來一下就能寫對的代碼,最后復(fù)雜不堪,出現(xiàn)很多bug。
世界上有兩種“沒有bug”的代碼。一種是“沒有明顯的bug的代碼”,另一種是“明顯沒有bug的代碼”。第一種情況,由于代碼復(fù)雜不堪,加上很多測試,各種coverage,貌似測試都通過了,所以就認(rèn)為代碼是正確的。第二種情況,由于代碼簡單直接,就算沒寫很多測試,你一眼看去就知道它不可能有bug。你喜歡哪一種“沒有bug”的代碼呢?
根據(jù)這些,我總結(jié)出來的防止過度工程的原則如下:
- 先把眼前的問題解決掉,解決好,再考慮將來的擴(kuò)展問題。
- 先寫出可用的代碼,反復(fù)推敲,再考慮是否需要重用的問題。
- 先寫出可用,簡單,明顯沒有bug的代碼,再考慮測試的問題。?
更多Java培訓(xùn),Java視頻,Java教程盡在北京尚學(xué)堂,關(guān)注北京尚學(xué)堂官方微信,獲得一手Java最新知識。
更多猛料!歡迎掃描上方二維碼關(guān)注北京尚學(xué)堂官方微信公眾號(資料領(lǐng)取驗證消息:156)
本文作者北京尚學(xué)堂原創(chuàng)。如需轉(zhuǎn)載請聯(lián)系作者授權(quán),未經(jīng)授權(quán),轉(zhuǎn)載必究。
?
轉(zhuǎn)載于:https://my.oschina.net/u/2947706/blog/790346
總結(jié)
以上是生活随笔為你收集整理的北京尚学堂|程序员的智慧的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 梦到满屋子老鼠跑什么意思
- 下一篇: IT综合学习网站收集