作者 | Isaac Lyman

成都創(chuàng)新互聯(lián)主要業(yè)務(wù)有網(wǎng)站營(yíng)銷策劃、做網(wǎng)站、成都網(wǎng)站建設(shè)、微信公眾號(hào)開發(fā)、微信小程序定制開發(fā)、HTML5建站、程序開發(fā)等業(yè)務(wù)。一次合作終身朋友,是我們奉行的宗旨;我們不僅僅把客戶當(dāng)客戶,還把客戶視為我們的合作伙伴,在開展業(yè)務(wù)的過(guò)程中,公司還積累了豐富的行業(yè)經(jīng)驗(yàn)、全網(wǎng)整合營(yíng)銷推廣資源和合作伙伴關(guān)系資源,并逐漸建立起規(guī)范的客戶服務(wù)和保障體系。
譯者 | 崔皓
誰(shuí)都喜歡可讀性強(qiáng)的代碼,希望接手的代碼容易閱讀,容易理解,從而減少交接的工作量,但并不是所有的代碼都有好的易讀性,接手前輩的“屎山”通常是一件令開發(fā)者非常痛苦的事情。
關(guān)于代碼有一種流行說(shuō)法:代碼被閱讀的次數(shù)是它被書寫次數(shù)的十倍,而且產(chǎn)品的壽命越長(zhǎng),這個(gè)比例就越高??紤]到這點(diǎn),我們似乎對(duì)“理解代碼”的投資明顯不足。開發(fā)者通常更側(cè)重于編碼的能力,而不是閱讀和解釋已有代碼的能力,即便這種場(chǎng)景在日常工作中會(huì)頻繁出現(xiàn)。
開發(fā)任務(wù)的前80-95%時(shí)間應(yīng)該用來(lái)閱讀代碼以及文檔。在研究現(xiàn)有代碼的過(guò)程中,你可能會(huì)學(xué)到很多東西,只有讀完代碼之后才能說(shuō):“這個(gè)功能已經(jīng)存在了,或者是加入這個(gè)功能弊大于利”。
本文將為你介紹一些實(shí)用的代碼閱讀策略,你可以根據(jù)實(shí)際情況使用它們。
一、重構(gòu)局部變量和方法
有時(shí)候,一段代碼非常模糊會(huì)誤導(dǎo)讀者甚至讓人難以推理出其含義。一個(gè)幾乎沒有風(fēng)險(xiǎn)的方法是重新命名局部變量和私有方法,以更準(zhǔn)確地描述它們的作用。這些類型的修改不會(huì)影響到當(dāng)前工作文件之外的功能,只要注意避免命名沖突,就不會(huì)導(dǎo)致邏輯錯(cuò)誤。如果可能的話,使用IDE的重構(gòu)工具(而不是文本查找和替換),這樣就可以一鍵重命名所有被使用的東西了。
例如,考慮下面這段JavaScript代碼
function ib(a, fn) {
return (a || []).reduce((o, i) => {
o[fn(i)] = i;
return o;
}, {});
}它閱讀起來(lái)非常困難,方法名ib對(duì)理解函數(shù)功能毫無(wú)用處。不過(guò),這并不妨礙你對(duì)它做出推斷:
由于reduce是在a上被調(diào)用的(并且它返回到一個(gè)空數(shù)組),a應(yīng)該是一個(gè)數(shù)組類型。
回調(diào)參數(shù)i將是該數(shù)組的一個(gè)元素。
reduce的第二個(gè)參數(shù),一個(gè)空的對(duì)象{},告訴我們回調(diào)參數(shù)o是一個(gè)字典(對(duì)象)。
所以,通過(guò)重命名,我們可以得到如下結(jié)果:
function ib(array, fn) {
return (array || []).reduce((dict, element) => {
dict[fn(element)] = element;
return dict; }, {});
}通過(guò)上面的調(diào)整,可以看到fn是把數(shù)組元素變成字典的鍵。這就揭示了函數(shù)ib的目的:將數(shù)組轉(zhuǎn)化為字典,用一個(gè)自定義的回調(diào)來(lái)確定索引每個(gè)元素的鍵。你可以把fn改名為getKey,而ib應(yīng)該被命名為indexBy。重新命名一些標(biāo)識(shí)符有助于我們理解代碼,而不需要改變它的邏輯,也不需要一下子考慮所有的部分。如果可以的話,強(qiáng)烈推薦修改。畢竟這樣可以提高代碼的可讀性,將使整個(gè)團(tuán)隊(duì)受益,同時(shí)它并沒有增加或改變程序的功能。
二、搞清楚代碼是如何被調(diào)用的
大多數(shù)代碼都會(huì)被其他代碼調(diào)用。如果你在糾結(jié)一段代碼,那么搞清楚它的調(diào)用情況對(duì)于了解它的功能有非常大的幫助。可以將方法重命名為ThisBreaksOnPurpose。然后進(jìn)行編譯,盡管在通過(guò)反射訪問的情況下,你在運(yùn)行時(shí)才會(huì)看到錯(cuò)誤,但編譯的錯(cuò)誤提示會(huì)告訴這個(gè)方法在哪里被使用。
如果以上方法不可行,你可以通過(guò)文本搜索方法名。如果你很幸運(yùn),這個(gè)方法的名字在代碼庫(kù)中是唯一的。如果不是這樣,你可能會(huì)得到一個(gè)更大的結(jié)果集,并且不得不翻閱大量不相關(guān)的代碼。
三、搜索類似的代碼
有時(shí),即使所有的標(biāo)識(shí)符都被很好地命名,用例也很清晰,但是代碼還是很難理解。不是所有的代碼都符合編碼習(xí)慣。有時(shí)某個(gè)特定的操作并沒有遵循編碼習(xí)慣。在最壞的情況下,有問題的代碼出現(xiàn)在工作的代碼庫(kù)中,同時(shí)也沒有使用明顯的慣用語(yǔ)。
然而真正獨(dú)特的代碼在長(zhǎng)期存在的代碼庫(kù)中是很少見的,特別是在單個(gè)表達(dá)式或代碼行上。如果花幾分鐘時(shí)間在項(xiàng)目中搜索類似的代碼,你可能會(huì)發(fā)現(xiàn)一些蛛絲馬跡來(lái)解開整個(gè)謎題。
全文搜索是其中最簡(jiǎn)單的方法。你可以選擇一個(gè)突出的代碼片段進(jìn)行搜索,搜索工具通常包括一個(gè) "全詞 "搜索選項(xiàng),這意味著搜索care.exe不會(huì)返回scare.exertion這樣的結(jié)果。如果你想進(jìn)一步縮小范圍,可以用正則表達(dá)式而不是文本短語(yǔ)進(jìn)行搜索。
當(dāng)然,偶爾即使是正則表達(dá)式也不足以縮小范圍,沒有人愿意花幾個(gè)小時(shí)在搜索結(jié)果中尋找可能沒有幫助的東西。學(xué)習(xí)一些高級(jí)搜索技術(shù)也是值得的。許多程序員喜歡使用Unix的命令行工具,如grep和awk,或者在Windows上使用手寫的PowerShell腳本。我的首選是JS Powered Search,這是一個(gè)VS Code擴(kuò)展,可以讓你在JavaScript中定義一個(gè)邏輯搜索查詢。
四、運(yùn)行單元測(cè)試
在一個(gè)完美的代碼庫(kù)中,你可以通過(guò)使用單元測(cè)試了解代碼運(yùn)行的狀態(tài)。但是大多數(shù)代碼庫(kù)并不完美;由于效率的原因,單元測(cè)試工作往往顯得可有可無(wú),有時(shí)單元測(cè)試所描述的是過(guò)時(shí)的行為。盡管如此,檢查并執(zhí)行代碼測(cè)試仍舊是一個(gè)好主意。至少,他們會(huì)描述代碼的輸入和輸出。
如果沒有單元測(cè)試或者單元測(cè)試不夠全面,你還有第二次挽救的機(jī)會(huì)。可以編寫一兩個(gè)測(cè)試來(lái)證明代碼是否存在的問題。如果發(fā)現(xiàn)問題并修復(fù)它然后提交修改,增加代碼庫(kù)的穩(wěn)定性,讓這段代碼具有自解釋的能力。你永遠(yuǎn)不必?fù)?dān)心增加自動(dòng)化測(cè)試會(huì)破壞現(xiàn)有的功能。
測(cè)試需要花費(fèi)時(shí)間來(lái)編寫,但此舉可以大大提升代碼執(zhí)行效率。測(cè)試是代碼正常工作的實(shí)際證據(jù),有單元測(cè)試在你就會(huì)相信代碼功能不會(huì)被破壞。
五、使用Debugger工具
一旦有了單元測(cè)試,就有了很好的機(jī)制幫助你進(jìn)行逐步的調(diào)試。設(shè)置一個(gè)斷點(diǎn)或在這段代碼的頂部添加一個(gè)斷點(diǎn)/調(diào)試器語(yǔ)句。然后運(yùn)行測(cè)試。一旦碰到了斷點(diǎn),執(zhí)行就會(huì)暫停,你可以每次前進(jìn)一行,進(jìn)入和退出函數(shù),并檢查范圍內(nèi)所有變量的值。
如果你知道哪些用戶行為觸發(fā)了相關(guān)的代碼,你就可以設(shè)置斷點(diǎn)并正常運(yùn)行程序,與程序的界面進(jìn)行交互。如果你這樣做,反饋回路會(huì)更長(zhǎng),但它也會(huì)使用更真實(shí)的數(shù)據(jù),這可能有助于你發(fā)現(xiàn)空引用和邊緣案例。
從上到下的逐行調(diào)試可能對(duì)運(yùn)行幾十或幾百次的代碼不太有用,比如嵌套的循環(huán)。對(duì)于這樣的代碼,可以在每個(gè)循環(huán)中添加匯總的變量,方便在循環(huán)結(jié)束的時(shí)查看總量。許多集成開發(fā)環(huán)境還允許你設(shè)置條件性斷點(diǎn),可以通過(guò)設(shè)置條件在循環(huán)中暫停并進(jìn)入斷點(diǎn)從而查看對(duì)應(yīng)變量的值。
六、搜索知識(shí)庫(kù)
如果你的團(tuán)隊(duì)把編寫文檔作為開發(fā)過(guò)程的一部分,你可以快速跳過(guò)這一步。文檔不應(yīng)該是唯一的真理來(lái)源,你應(yīng)該依靠代碼來(lái)了解程序的行為方式。
文檔雖然可以解釋代碼的 "How",但它往往更擅長(zhǎng)解釋 "Why"。有時(shí)你明白一段代碼在做什么,但從另一個(gè)角度看貌似有些不對(duì)。所以在改變它之前,你應(yīng)該盡一切努力去了解原來(lái)的程序員是根據(jù)什么信息或約束來(lái)編碼的。
一篇好的內(nèi)部文檔也能為你指出知道真相的隊(duì)友。如果你已經(jīng)走到了這一步,做了足夠多的工作,那么可以向外尋求幫助。確保讓對(duì)方知道你在做什么工作,你想解決什么問題,他們很有可能會(huì)注意到你的視野盲區(qū)。
七、使用版本控制注釋
看到這里,你已經(jīng)了解了幾種有效的代碼閱讀策略。但即使如此,也可能會(huì)有無(wú)法解決的問題:一個(gè)奇怪的設(shè)計(jì)決定,一個(gè)打破代碼庫(kù)編碼模式的方法,一個(gè)沒有明顯理由的代碼特質(zhì)。
版本控制系統(tǒng)可以顯示代碼庫(kù)中任何一行代碼的作者和提交。在Git中,就是git blame命令。大多數(shù)系統(tǒng)稱它為"blue"或"annotate"。你可以在命令行或IDE中運(yùn)行這個(gè)命令。出現(xiàn)的將是一個(gè)逐行的提交列表:一個(gè)提交哈希值,一個(gè)提交信息,以及一個(gè)作者。
如果該行代碼的最近一次提交沒有意義——例如它是一個(gè)格式化或空白的變化,就需要通過(guò)文件的變更歷史來(lái)找到引入該行代碼的提交。同樣,版本控制系統(tǒng)有一些工具可以幫助你做到這一點(diǎn)。
一旦你拿到了PR和Ticket,不僅擁有了代碼的背景,還可以找到與之相關(guān)的工作人員:代碼的作者、PR審核者、任何評(píng)論或更新Ticket的人、簽署QA的人。如果前幾種方法都不奏效,那么是時(shí)候該和前輩們聊聊了。
八、先理解,再編碼
通過(guò)對(duì)以上步驟的學(xué)習(xí),或許對(duì)你有所幫助,特別是對(duì)代碼背景的理解以及功能的實(shí)現(xiàn)方面。在你繼續(xù)前進(jìn)之前,還需要考慮重構(gòu)代碼以使其清晰,創(chuàng)建新的文檔,在這里投入的任何時(shí)間都會(huì)讓你和你的團(tuán)隊(duì)在代碼的互動(dòng)中獲得回報(bào)。
有效閱讀代碼的能力是一種秘密武器,它可以使你快速通過(guò)技術(shù)面試,并使你成為任何團(tuán)隊(duì)的重要成員。擅長(zhǎng)寫代碼的程序員是有價(jià)值的,擅長(zhǎng)讀代碼的程序員就更具價(jià)值了。當(dāng)生產(chǎn)中出現(xiàn)錯(cuò)誤或急需開發(fā)新功能時(shí),第一步也是最重要的一步就是理解,閱讀代碼是能讓你順利到達(dá)彼岸。
網(wǎng)站欄目:接手爛代碼,不用對(duì)上任客氣!
網(wǎng)站地址:http://m.5511xx.com/article/djsejde.html


咨詢
建站咨詢
