程序員,請(qǐng)優(yōu)先提高代碼的可讀性
現(xiàn)在,當(dāng)有人提及“優(yōu)化”一詞時(shí),他們通常是指“優(yōu)化執(zhí)行時(shí)間”,除非他們明確表明要優(yōu)化GPU的內(nèi)存消耗,網(wǎng)絡(luò)流量等等。
了解優(yōu)化對(duì)象
當(dāng)我開始編程時(shí),所擁有的處理器執(zhí)行速度很慢,內(nèi)存空間也非常有限 —— 有時(shí)僅以KB衡量。因此,必須合理考慮內(nèi)存的使用和優(yōu)化。
在大學(xué)里,我們知道了優(yōu)化的兩個(gè)極端情況:
- 你可以犧牲空間來(lái)?yè)Q取執(zhí)行速度的提升,
- 或者通過(guò)執(zhí)行重復(fù)操作來(lái)?yè)Q取內(nèi)存消耗的優(yōu)化。
如今,沒(méi)有人會(huì)太在意內(nèi)存的使用(除了demoseners,嵌入式系統(tǒng)工程師,部分手游開發(fā)者),不僅是對(duì)于RAM空間,硬盤空間也是。 試想一下僅安裝看門狗就耗費(fèi)硬盤近25Gb 空間。 此外,我在谷歌瀏覽器選項(xiàng)卡中寫這篇文章時(shí),占用了130Mb的RAM空間。
實(shí)際上需要優(yōu)化的對(duì)象有很多:
- 隨著智能手機(jī)市場(chǎng)的增長(zhǎng),電量損耗的優(yōu)化備受關(guān)注;
- 優(yōu)化可讀性可以讓代碼易于閱讀和調(diào)試,從而縮短開發(fā)周期,降低開發(fā)成本;
- 還有很多優(yōu)化類型,此處不再贅述……
優(yōu)化可讀性——讓代碼更容易閱讀、跟蹤和理解。
你應(yīng)該明白,你在優(yōu)化時(shí)難以兼顧各個(gè)方面。 例如,當(dāng)致力于性能優(yōu)化時(shí),你很可能讓應(yīng)用程序內(nèi)存消耗增加,同時(shí)代碼可讀性也變差。
為何優(yōu)化可讀性
開發(fā)者大量工作時(shí)間并不是在編寫代碼,而是閱讀代碼,調(diào)試代碼,查閱他人提交的開發(fā)文檔,學(xué)習(xí)新的庫(kù)等。
當(dāng)閱讀代碼時(shí),開發(fā)者實(shí)際上是充當(dāng)代碼解釋器的角色(雖比不上計(jì)算機(jī))—— 在他們的頭腦中執(zhí)行代碼,并試圖記住當(dāng)前執(zhí)行狀態(tài)。 這就是程序員在閱讀代碼過(guò)程中被打攪脾氣暴躁的原因。
時(shí)間== 金錢
你應(yīng)該意識(shí)到一件很最重要的事,是你和你的同事浪費(fèi)了大量時(shí)間。 即使是一個(gè)努力工作的開發(fā)者,在做下面的事時(shí)仍然浪費(fèi)了大量時(shí)間:
- 實(shí)現(xiàn)一些現(xiàn)在不需要,以后也可能永遠(yuǎn)用不到的功能。
- 做一些沒(méi)有實(shí)際價(jià)值的改進(jìn)。 例如,花費(fèi)一周時(shí)間優(yōu)化一個(gè)函數(shù)的執(zhí)行時(shí)間,而該函數(shù)在1小時(shí)內(nèi)僅被調(diào)用10ms的時(shí)間。
- 編寫的代碼難以調(diào)試,卻還要試圖從中找出錯(cuò)誤。
- 編寫的代碼他人難以理解。 注意,“他人”也可能是短短一周后的你。
上述情況是假設(shè)遇到問(wèn)題的開發(fā)者經(jīng)驗(yàn)豐富并且熟知高效算法和簡(jiǎn)潔代碼如何書寫,否則要列出的情況要更多。
優(yōu)化可讀性
唐納德·克努特說(shuō)過(guò)一句名言。 我敢打賭你聽過(guò)很多次。
“在編程中,過(guò)早優(yōu)化是萬(wàn)惡之源。 ” —— D.Knuth,1974
我遇到很多知道這句話的人,但真正理解這句話的卻很少。 最常見的錯(cuò)誤理解像這樣:
- —為何這么簡(jiǎn)單的任務(wù),代碼卻如此復(fù)雜?
- —我優(yōu)化了X和Y,因?yàn)樵趯?lái)……
- —難道你沒(méi)聽說(shuō)過(guò)早的優(yōu)化是萬(wàn)惡之源嗎?
- —當(dāng)然,但這并不是過(guò)早優(yōu)化,我能肯定這樣做程序執(zhí)行效率會(huì)更高。
我想這是由于對(duì)過(guò)早優(yōu)化這個(gè)詞沒(méi)有明確界定的原因。 這就是這些人一點(diǎn)也不認(rèn)為他們那么做屬于過(guò)早優(yōu)化的原因。 那么,我們?cè)撊绾谓缍ㄟ@個(gè)詞呢?
過(guò)早優(yōu)化——在工作系統(tǒng)中分析和運(yùn)行測(cè)試前的任何優(yōu)化嘗試。
除可讀性之外任何修改都屬于過(guò)早優(yōu)化。 所以,與其說(shuō)一個(gè)人不應(yīng)該做什么,不如說(shuō)應(yīng)該做什么。 那么,這句引言可以這樣理解:
優(yōu)先提高可讀性。
什么阻礙了開發(fā)者閱讀代碼
好吧,我們一致認(rèn)為,我們應(yīng)該讓代碼更易于閱讀,這樣可以節(jié)約時(shí)間和金錢,對(duì)吧?但這究竟意味著什么?
有跡象表明,下面兩個(gè)基本方面極大地降低了開發(fā)者閱讀代碼的速度:
- 代碼晦澀難懂,
- 代碼難以跟蹤。
代碼艱澀難懂
遺憾的是,人們并不能像軟件解釋器那樣,可以不必理會(huì)將兩個(gè)數(shù)相加并調(diào)用一個(gè)函數(shù)這部分代碼的功能(機(jī)械式的編譯)。
為了查找代碼異常的原因,程序員必須理解源程序中編寫的代碼實(shí)現(xiàn)了何種功能,編寫的初衷是為了實(shí)現(xiàn)何種功能。
什么讓代碼晦澀難懂?
下面情況是對(duì)于經(jīng)驗(yàn)豐富的開發(fā)者而言,這些開發(fā)者熟悉代碼開發(fā)使用的語(yǔ)言和程序中使用的算法(即他們有足夠的知識(shí)來(lái)理解這段代碼)。
- 代碼不良。 單個(gè)字母的奇怪變量和1000行代碼的冗長(zhǎng)函數(shù)。
- 代碼的格式不正確或不一致。
- 代碼中包含冗余代碼。
- 代碼中包含未備注的低層次優(yōu)化。
- 代碼過(guò)于高明。
我將跳過(guò)前兩條,因?yàn)闊o(wú)論如何你不應(yīng)該閱讀不良代碼。 如果你所在的公司有人編寫了不良代碼,你應(yīng)該糾正它們或者將其廢棄。 當(dāng)然,你必須為你的整個(gè)代碼庫(kù)執(zhí)行嚴(yán)格的編程規(guī)范。
3. 代碼中包含冗余代碼
亦或所謂的行數(shù)優(yōu)化。 嵌套函數(shù)調(diào)用和條件運(yùn)算符的長(zhǎng)行代碼難以剖析。 當(dāng)然,你可能會(huì)說(shuō)這種觀點(diǎn)是片面的。 但這些人覺(jué)得源程序代碼越短越好,不必考慮可讀性。
4. 未備注地層次優(yōu)化
最初,代碼的可讀性很好,工作也很穩(wěn)定,但有些人決定在某些方面對(duì)其進(jìn)行優(yōu)化。 經(jīng)過(guò)認(rèn)真剖析,這可能是一個(gè)很好的優(yōu)化,但此時(shí)的代碼看上去像是數(shù)組、位運(yùn)算和幻數(shù)的結(jié)合體。 沒(méi)有人知道代碼在做什么,甚至代碼應(yīng)該做什么,因?yàn)橥瓿蓛?yōu)化的人沒(méi)有提交任何說(shuō)明。
也許你聽說(shuō)過(guò)優(yōu)秀的代碼不需要說(shuō)明文檔。 但是經(jīng)過(guò)優(yōu)化的代碼(特別是優(yōu)化效果很理想的情況)必須要有說(shuō)明文檔。
在你的代碼庫(kù)中,可能大部分的優(yōu)化只是像這樣的未備注行:
- if (val != val) { ... }
5. 代碼過(guò)于高明
作為軟件開發(fā)者,我們掌握越來(lái)越多的學(xué)術(shù)技巧并將其運(yùn)用到實(shí)際代碼開發(fā)中。 畢竟,我們是計(jì)算機(jī)科學(xué)家,而不只是碼農(nóng)!
有些語(yǔ)言甚至鼓勵(lì)開發(fā)者使用前沿技術(shù),使代碼更具表現(xiàn)力和學(xué)術(shù)性。 當(dāng)你用代碼建立了一個(gè)非常健壯的系統(tǒng),特別當(dāng)你用數(shù)學(xué)方法證明了一個(gè)高深定理,而99.997%受過(guò)教育的人對(duì)這種方法都不理解,你就會(huì)有這種成就感。
即使代碼被良好地封裝成模塊/類/函數(shù)并且這些模塊包含完全可讀的命令式代碼,但其他人想要讀懂這段代碼,他們必須掌握整個(gè)代碼的框架以及所有使用的相關(guān)技術(shù)和模式。
再一次強(qiáng)調(diào),記住“其他人”可能就是一周后的你。
極可能這是我在工作中僅認(rèn)識(shí)兩個(gè)使用Scala語(yǔ)言人的原因。就我個(gè)人而言,非常喜歡Scala語(yǔ)言。 對(duì)我來(lái)說(shuō),它就是一個(gè)學(xué)術(shù)操場(chǎng),我可以在那里建造玻璃城堡。 一旦你越了解它,它的越多特性也就能為你所用,你也就越明白它本質(zhì)上只是一門編程語(yǔ)言(請(qǐng)不要在這里引用我?。?。
雖不如Perl語(yǔ)言,但即使最漂亮的代碼庫(kù)也需要修改和更新。 現(xiàn)在,你需要尋找一個(gè)能夠理解這些優(yōu)美代碼的人……
簡(jiǎn)潔高明的代碼難以閱讀似乎是有爭(zhēng)議的。
“軟件調(diào)試要比編寫代碼困難一倍,如果你發(fā)揮了***才智編寫代碼,那么你的智商便不足以調(diào)試這個(gè)代碼。 ” —— Brian Kernighan
#p#
代碼難以跟蹤
閱讀代碼時(shí),通常需要頻繁的從一個(gè)函數(shù)或類跳轉(zhuǎn)到另一個(gè)函數(shù)或類。 掌握你使用的集成開發(fā)環(huán)境(IDE),可以節(jié)約很多閱讀時(shí)間。 通過(guò)使用集成開發(fā)環(huán)境(例如Visual Studio)的“跳轉(zhuǎn)至聲明”,“查找使用”,“導(dǎo)航至”,“檢查”等特性,你可以將整個(gè)代碼看作是一幅連通圖。
在Notepad中編寫代碼是不錯(cuò)的選擇,但是如果你想有效的閱讀代碼,必須掌握一個(gè)集成開發(fā)環(huán)境。
那么,究竟什么是連通圖呢?
連通圖是在拓?fù)淇臻g中連接的圖,即圖中任意兩點(diǎn)之間都有一條通路。(來(lái)源)
換句話來(lái)說(shuō),在“連通”代碼中,你可以方便的從一個(gè)方法中跟蹤到另一個(gè)方法中,并在你頭腦中建立這段代碼的功能框架。
如果代碼中某一部分鏈接被破壞(在這種情況下,集成開發(fā)環(huán)境不能幫助你實(shí)現(xiàn)函數(shù)間的跳轉(zhuǎn)),通常你必須花一些時(shí)間自己查找鏈接。代碼中被破壞的鏈部分越多,越難以跟蹤,代碼也就越難以閱讀。
那么,為什么代碼圖會(huì)被斷開?原因是多方面的,下面將列出一些常見情況:
1. 使用字符串引用方法和屬性
一些框架就喜歡這樣做,他們將”回調(diào)”作為字符串傳遞并在需要時(shí)使用反射。 此時(shí)你需要使用CMD+F查找。
最可惡的是動(dòng)態(tài)語(yǔ)言中的動(dòng)態(tài)字符串…… 對(duì)這個(gè)問(wèn)題,向JavaScript或AS3致敬!
2. 代碼被分割成互不相連的部分
例如,你的代碼一半使用C#編寫,另一半是在可視化節(jié)點(diǎn)編輯器生成。 在這兩者之間跳轉(zhuǎn)非常不易。
依賴注入框架和其他XML配置工具也是。雖然沒(méi)有明確說(shuō)明,但編寫XML配置文件也屬于編程。 這就是所謂的的聲明式編程(更不用說(shuō)那些構(gòu)建基于XML命令式語(yǔ)言的瘋狂的人)。
3. 巨大的圖節(jié)點(diǎn)
20個(gè)鏈接跳轉(zhuǎn)到這個(gè)包含1000行代碼的函數(shù)?。。哎喲。 你不需要包含這種節(jié)點(diǎn)的圖。
4. 一切都過(guò)于抽象
通過(guò)跳轉(zhuǎn)至聲明,你可到達(dá)一個(gè)接口或者一個(gè)抽象類,必須弄清楚有哪些實(shí)現(xiàn)。 依賴注入,抽象工廠和其他所有反對(duì)依賴的方法使得這一切變得更糟。 代碼圖中節(jié)點(diǎn)間的聯(lián)系過(guò)于抽象。
這樣說(shuō)來(lái),我討厭依賴注入(DI)和擴(kuò)展標(biāo)識(shí)語(yǔ)言(XML)。但DI是一個(gè)很棒的工具,它可以讓你避免書寫面條式代碼并讓程序的架構(gòu)更加模塊化,更具可測(cè)試性。但像其他好的事物一樣,過(guò)度依賴必然產(chǎn)生負(fù)面效果。
我曾在審查一個(gè)應(yīng)用程序時(shí)感到完全氣餒,因?yàn)槲乙庾R(shí)到自己弄不明白程序從何處開始。。。例如它的入口點(diǎn)在哪。 這一切都是在程序開始時(shí)從XML配置工具自動(dòng)生成。
但我確實(shí)討厭XML配置工具。
***
所以,到這里你應(yīng)該已經(jīng)學(xué)會(huì):
- 掌握你的集成開發(fā)環(huán)境,
- 盡可能保持代碼圖連通,
- 首先編寫簡(jiǎn)單代碼,
- 編寫不必要的代碼,就是在浪費(fèi)金錢。
強(qiáng)迫自己編寫簡(jiǎn)單的代碼,避免在早期階段優(yōu)化確實(shí)有一定難度,這需要花費(fèi)時(shí)間。
在截止期前2小時(shí)已連續(xù)工作48小時(shí),如果你在半睡半醒的狀態(tài)下能夠閱讀你所使用的代碼,你應(yīng)該對(duì)過(guò)去的自己說(shuō)聲“謝謝”。
附言
不要錯(cuò)過(guò)reddit和hackernews上的精彩討論。
非常感謝/u/Arandur糾正了大量語(yǔ)法錯(cuò)誤!
(備注:限于譯者的水平,譯文中肯定有錯(cuò)誤和不妥之處,懇請(qǐng)批評(píng)指正?。?/p>
本文由 伯樂(lè)在線 - ashiontang 翻譯自 Valentin Simonov。