高效能編碼人士的7個習(xí)慣
本文討論的是編碼風(fēng)格、原則和藝術(shù)。
至于如何編碼實現(xiàn)系統(tǒng)的功能和性能、如何架構(gòu)設(shè)計、如何提高易用性、如何團隊協(xié)作、如何項目管理,均不在本文討論范疇之內(nèi)。
本文也不講大括號應(yīng)該放在哪里,代碼如何縮進,運算符周圍是否加上空格這種規(guī)范,雖然這也很重要。
建議有7年以上編碼經(jīng)驗的程序員閱讀本文,編程年頭太少的,很難理解本文。
如果你持續(xù)編碼已經(jīng)15年以上,但對本文沒有共鳴,建議好好考慮一下你是否適合編程。
編碼風(fēng)格的所有目標(biāo)
1、代碼易讀
2、代碼易改
這就足夠了。
而且這很難做到。
最重要的技巧
1、小函數(shù);(注意這個技巧排在第一位)
2、小文件、小類;(小的東西,易讀,易改)
3、做好單元測試!(所有都測到了,你就敢改)
4、少注釋;(而是增加表達力!讓人一眼看懂的代碼不需要注釋)
5、消除重復(fù)。(重復(fù)永遠是丑陋的)
如果你記不住這么多條,就記一條:小函數(shù)。
七個習(xí)慣
一、保持精簡
“小就是美”。
告訴你一個秘訣,即便你不會編碼,也可以簡單而準(zhǔn)確地判斷一個人是不是編程高手。
你只需要略微看一下他的代碼就可以。
如果他代碼中的函數(shù),絕大多數(shù)都在20行以內(nèi),這就是高手。
如果基本都在10行以內(nèi),這是頂尖高手。
如果都在3、4行左右,這是大師。
如果有很多超于100行的函數(shù),這是新手、低手。
類似地,看他代碼中每個文件的長度,應(yīng)該大多都在200行或者400行以內(nèi),才是正常的,雖然這無法直接判斷是不是高手。
但如果動輒都在2000行以上,這是新手、低手。
大致而言,高手能做到:
1、小函數(shù)、小文件、小類;
2、小的單元測試用例。
3、函數(shù)有盡量少的參數(shù)。
最理想的函數(shù)參數(shù)數(shù)量是0個,其次是1個,再次是2個,應(yīng)盡量避免3個及以上。
參數(shù)越少,越好測試,越不容易搞錯參數(shù)的順序。
(為減少參數(shù),可考慮封裝成類。)
怎么做到這點?只要你有保持精簡的意識,你總會做到的。
任何一個文件,一個函數(shù),只要感覺它大到一定程度(比如超過10行),就著手拆分。
下面舉個例子:

上圖左邊是一個長達15行的函數(shù),經(jīng)過整理后,可以寫成右邊那樣,但這仍然不夠。
右邊每種顏色的代碼塊都應(yīng)該由一個函數(shù)完成,所以,左邊這個函數(shù),最終變成4行。(每行都是一個函數(shù)調(diào)用)
類似的,稍微看到一點比較繁復(fù)的東西,就抽象它,精簡它,讓你的程序看上去非常簡潔才可以。
這就是在踐行KISS原則。(Keep It Simple,Stupid)
也是在踐行“松耦合、高內(nèi)聚”原則。(一個小函數(shù)、小類,必然是高度內(nèi)聚的)
二、提高表達力
編寫整潔、易讀的代碼。
高效能人士寫的代碼,即便在10年后,他把代碼已經(jīng)忘光光的時候,只要需要,他拿出來一看,就能輕松理解。
我寫代碼,從來不是為了別人看,我只是怕自己忘記。
我的記性不太好,而且我從來不依賴我的記性,所以,我必須寫得非常易讀。我很難忍受看不懂自己代碼而產(chǎn)生的那種嫌棄感。(不是嫌棄現(xiàn)在的自己,而是以前的自已)
“代碼的寫法應(yīng)當(dāng)使別人理解它所需的時間最小化。”
如何做到?下面談5點。
1、讓代碼讀起來像自然語言。(不斷磨練這點)
2、高度重視變量和函數(shù)的命名,要明確、無歧義。
所有函數(shù)命名,都以動詞為開頭。(類名用名詞)
不要怕命名長,只要有助于理解,長點沒事。
慎重選擇用詞,考慮不會讓人誤解。舉個例子:

上面這段代碼是拷貝字符串的,如果參數(shù)名改為source和destination,就會清晰很多,而且,引用者會不太容易搞錯參數(shù)順序。
3、在實在沒有辦法用代碼表達意圖的時候,寫注釋。
除去版權(quán)和許可證這種必要的注釋,絕大多數(shù)情況下,我只在覺得自己以后可能看不懂的情況寫注釋。
在比較古板的條條框框下,注釋應(yīng)該占代碼的1/5以上。
然而,對高手而言:在大多數(shù)情況下,注釋表明了一種失敗。
所以,在你想注釋的時候,再努力一下,看能不能僅用代碼就可以明白表達。
“不要給不好的名字加注釋——應(yīng)該把名字改好”注釋不是代碼,所以它是難以管理和測試的。由于人的懶惰天性,注釋通常不會跟著代碼的改動而改動,所以注釋往往是過時的、錯誤的(而且難以發(fā)現(xiàn))。
“不準(zhǔn)確的注釋要比沒注釋壞得多。它們滿口胡言。”
4、照顧閱讀者的感受
把關(guān)系相近的函數(shù),放到一起。
如果函數(shù)A調(diào)用函數(shù)B,那么把A放到B前面,人就會自然閱讀下去。而且閱讀者會有一個預(yù)期,看到一個新的函數(shù),知道很快就能讀到它的實現(xiàn)。
(不過我的習(xí)慣是:把B放到A前面,因為有些編程語言,要求一個函數(shù)只能調(diào)用前面出現(xiàn)過的函數(shù),如果不加聲明的話。所以,看我的程序,需要從文件底部看起。)
同樣地,概念相關(guān)的代碼,放到一起,用空行分開。
不要再用微軟提倡的匈牙利標(biāo)記法(比如szAuthor這種命名)
,那已經(jīng)過時了。使用大小寫或者下劃線分割單詞的命名(比如deletePage)
就很好,匈牙利標(biāo)記法比較丑陋,直接影響閱讀感受。
再舉一個例子:為了便于閱讀,避免使用do/while語句。
do/while這種寫法很丑陋,通常來講,邏輯條件應(yīng)該出現(xiàn)在它們所“保護”的代碼之前,if/while和for語句就是這么干的。而do/while把條件放在后面,這很反常,以至于大多數(shù)人讀到do/while時,都需要讀兩遍。
5、不要重復(fù)!
“重復(fù)可能是軟件中一切邪惡的根源。
“重復(fù)”會浪費編碼者的時間、浪費閱讀者的時間、浪費維護者的時間、浪費編譯、運行的時間。而且,還浪費存儲。
看到重復(fù),就改寫它,這是一個基本素質(zhì)。
三、邊寫邊測
程序如果易讀,就很有利于修改。
但僅僅“易讀”是遠遠不夠的。
很多程序員敢寫代碼,但不敢改,他們都知道什么是“牽一發(fā)而動全身”。
所以,每次你讓他們改程序的時候,他們的臉色都不好看的。
只有頂尖高手,才會“欣然改程序”,他們知道,這又是一次展示自己能力的機會。
他們是怎么做到這點的?
他們的底氣來自測試。
測試覆蓋率越高,你就越放心,你就可以放心地修改,甚至放心地改架構(gòu)!
下面是TDD(來自極限編程)
的方法論,值得參考和借鑒。(說實話,我并沒有嚴(yán)格做到)
1.在寫功能代碼前,先寫一個小的單元測試。(不要寫太多,否則你會沒有動力)
2.運行所有測試(先看這個測試是否能工作),測試應(yīng)該失敗。(因為還沒有寫生產(chǎn)代碼)
3.編寫僅僅能通過這個測試的最簡單代碼。(先不用寫很好,后面還會重構(gòu);也不要寫更多,因為其他還沒寫測試)
4.通過所有測試。(注意是所有測試,這保證了一切都是對的。)
5.根據(jù)需要重構(gòu),每次重構(gòu)后都測試。(始終保持一切都是對的。)
6.重復(fù)上述循環(huán)。(測試和編碼應(yīng)該是小型和漸進的)測試用例應(yīng)該有這樣的結(jié)構(gòu):設(shè)置、執(zhí)行、驗證以及可能需要的清理。
測試用例應(yīng)該盡量小,一次只干一件事。
堅持TDD原則,你就會有覆蓋率足夠高的測試代碼,否則可就不一定。
注意:“測試代碼和生產(chǎn)代碼一樣重要。它可不是二等公民。它需要被思考、被設(shè)計和被照料。它該像生產(chǎn)代碼一般保持整潔。”
當(dāng)然,寫測試代碼本身是需要成本的,但是你要好好想想,值不值?
四、時時清理
所有東西,如果不維護,總是會變臟、變壞。(這就是這兩年很多人喜歡說的“熵增”)
代碼也一樣。(除非你再也不用它了)
所以,每次你改動代碼時,都要讓代碼變得更整潔。
正如美國童子軍軍規(guī):“讓營地比你來時更干凈。”
注意,你敢于清理的前提是,你有足夠的測試用例。
每次清理時,告誡自己,這會給自己帶來巨大好處:可以避免今后巨大的麻煩。
這可以用在工作和生活中的任何方面,包括你的思想。
五、同時保持兩種截然相反的思維
很多思維,都有截然相反的對立面,并且都有其擁躉。
比如有人說WEB3是未來,就有人說WEB3是垃圾。
有人說公司要人性化管理,就有人說必須要狼性管理。
再舉兩、三個例子:
1、應(yīng)該自頂向下編程,還是自底向上編程?
正確答案是,這兩種思維都要用。
2、先寫代碼,再寫測試;還是先寫測試,再寫代碼?
你自己看著辦。
3、“三目運算符”的可讀性也是有爭議的。
擁護者認(rèn)為這種方式簡潔,只有一行;反對者則說這可能會造成閱讀的混亂。
是下面這樣好,
?
還是下面這樣好?

正確做法:默認(rèn)情況下都用if/else,只在最簡單的情況下使用“三目運算符”。
保持兩種思維的好處在于,你可以始終對這兩種提法進行實踐、測試、反思,在兩種極端的說法中找到平衡,找到各自適用的場景,找到更適合自己的做法。
這會讓你更從容、更靈活。
甚至,不要一味追求“小就是美”,偶爾大一下也沒啥。
六、知道不做什么
最好讀的代碼就是沒有代碼。
如果有現(xiàn)成的,不要自己去做。
如果現(xiàn)在還用不到,就先不要寫。(YAGNI原則:You Ain’t Gonna Need It)
你所寫的每一行代碼都是要測試和維護的。
你要節(jié)省自己的時間,你還有更重要的事要做。
七、更新自我
自FORTRAN語言在上世紀(jì)50年代后期出現(xiàn)之后,在編程思想方面,人們就在不斷的學(xué)習(xí)和長進。
人們首先認(rèn)識到封裝的重要性,ALGOL語言的設(shè)計者在ALGOL60中采用了以Begin……End為標(biāo)識的程序塊,使塊內(nèi)變量名是局部的,以避免它們與程序中塊外的同名變量相沖突,這是編程語言中首次提供封裝的嘗試。
上世紀(jì)60年代后期,隨著《程序結(jié)構(gòu)理論》和《GOTO語句有害論》的提出,證明了任何程序的邏輯結(jié)構(gòu)都可以用順序結(jié)構(gòu)、選擇結(jié)構(gòu)和循環(huán)結(jié)構(gòu)來表示,確立了結(jié)構(gòu)化程序設(shè)計思想,這使得程序更容易維護。
上世紀(jì)80年代,面向?qū)ο蟪绦蛟O(shè)計思想經(jīng)過20年的研究和發(fā)展逐漸成熟,一大批面向?qū)ο笳Z言相繼出現(xiàn)。
2001年2月,在美國猶他州瓦薩奇山,17個來自于各類敏捷方法的實踐者共同達成了一個共識:《敏捷宣言》,提出了十二條敏捷原則。
其中第十條原則和本文密切相關(guān):
簡潔是敏捷的精髓,它是極力減少不必要工作的藝術(shù)。
Simplicity--the art of maximizing the amount of work not done--is essential.
然后,設(shè)計模式、重構(gòu)開始普遍流行。
再然后,有了CI/CD,有了Devops。
以前的程序員,是不懂這些的。
如果你是搞IT的,你就會發(fā)現(xiàn),編程思想、方法、語言、工具,層出不窮、學(xué)無止境。
真正的高效能人士,永遠不會停止學(xué)習(xí),永遠不會停止更新自我。
因為永遠會有更好的東西問世。
編程之外
我在命名這7個習(xí)慣時,刻意使用了普適的說法。
這些高度抽象的理論,完全可以用在其他領(lǐng)域。
1、保持精簡
2、提高表達力
3、邊寫邊測
4、時時清理
5、同時保持兩種截然相反的思維
6、知道不做什么
7、更新自我
這些一樣可以用在寫作、工作、生活以及做人上。
成熟的人,都明白這點。

圖|高效能編程人士的七個習(xí)慣
本文用標(biāo)題和圖示向Stephen R. Covey致敬,感謝他的神書《高效能人士的七個習(xí)慣》。
本文引用主要來自以下兩本廣為人知的名著:
Robert C. Martin. 代碼整潔之道. 人民郵電出版社.
Dustin Boswell,Trevor Foucher. 編寫可讀代碼的藝術(shù). 機械工業(yè)出版社.
文|衛(wèi)劍釩
