如何正確的閱讀源代碼?
寫完「你也可以像 Prisma 一樣渲染圖像」之后,有讀者提了這樣一個(gè)問(wèn)題:
我猜您平時(shí)應(yīng)該有閱讀開(kāi)源項(xiàng)目的源碼,好的開(kāi)源軟件或者框架,動(dòng)輒數(shù)萬(wàn)行的源碼,雖說(shuō)是寶藏,但我看源碼一直不得要領(lǐng),投入時(shí)間不少但收獲甚微,請(qǐng)教下:
您閱讀源碼的關(guān)注點(diǎn)一般有哪些?
您看源碼有沒(méi)有什么方法論呢,如何抓住重點(diǎn)下手?有時(shí)面對(duì)優(yōu)秀的開(kāi)源框架,想學(xué)習(xí),我甚至都不知從哪看起。
關(guān)于這個(gè)問(wèn)題,我說(shuō)兩句。
閱讀優(yōu)秀的源代碼是軟件工程師提高自己編程能力和學(xué)習(xí)開(kāi)源框架的***手段之一。作為一名運(yùn)動(dòng)員,除了持續(xù)的刻意練習(xí),還需要觀摩大量對(duì)手的比賽視頻。作為一名小說(shuō)家,除了筆耕不輟,還需要閱讀大量的其他作家的偉大作品。當(dāng)然,觀摩和閱讀不是目的,是手段。路遙在創(chuàng)作《平凡的世界》之前讀了大量的「名著」,然后,他把所有尊敬的作家都安放在遠(yuǎn)方歷史為他們準(zhǔn)備的「先圣詞」中,讓他們各自光芒四射,照耀大地,然后開(kāi)始創(chuàng)作百萬(wàn)巨著《平凡的世界》。照耀你的世界的光芒,應(yīng)該是自己發(fā)出的。
程序員亦是如此。在編程的路上,有無(wú)數(shù)的大師寫出了偉大的代碼和軟件,去學(xué)習(xí)他們的編程技巧和技術(shù)風(fēng)格,取其精華,去其糟粕,***完成自己的作品。2005年左右我有幸參與了一個(gè)類似 CORBA 的分布式應(yīng)用系統(tǒng)的開(kāi)發(fā),我在那段時(shí)間差不多通讀了這個(gè)項(xiàng)目的早期代碼,其整體架構(gòu)規(guī)劃和代碼設(shè)計(jì)的精巧程度讓人嘆為觀止(代碼的編寫者是早期的 CORBA 規(guī)范制定者)。這個(gè)經(jīng)歷對(duì)我后來(lái)的編程之路產(chǎn)生了深遠(yuǎn)的影響。
與編程一樣,閱讀別人的源代碼永遠(yuǎn)不是一件輕松的事,或者說(shuō),是一件困難的事情,需要持續(xù)的投入,閱讀、研究、把玩、實(shí)踐。很多人覺(jué)得拿到了源代碼就像買了本書一樣,放到書柜上,立刻就產(chǎn)生了一種學(xué)會(huì)了的錯(cuò)覺(jué)(我就是這樣,微笑),但真正實(shí)踐起來(lái)才會(huì)體驗(yàn)到強(qiáng)烈的挫敗感。大部分情況下,讀不下去,不是方法不好,而是投入度不夠。
閱讀源代碼,一定要找到好的開(kāi)源項(xiàng)目。什么是好的項(xiàng)目?口碑好且應(yīng)用廣泛的項(xiàng)目就是好項(xiàng)目,比如 Docker、Spring、OpenResty,都是非常好的閱讀素材。另外,完善的文檔和足夠的 test case 覆蓋率,都是衡量一個(gè)開(kāi)源項(xiàng)目是否優(yōu)秀的標(biāo)準(zhǔn)。很多人說(shuō),代碼即文檔,好的代碼本身就是自解釋的。但是,對(duì)于規(guī)模宏大的開(kāi)源軟件來(lái)說(shuō),沒(méi)有文檔是不可想象的。所以在閱讀源代碼之前,一定要讀文檔。盡管讀了文檔之后,你可能不知道代碼的技術(shù)細(xì)節(jié),但至少可以了解項(xiàng)目的輪廓。結(jié)合開(kāi)源項(xiàng)目的代碼目錄,差不多可以繪制出一個(gè)粗粒度的整體架構(gòu)圖,類似這樣的:
然后為每個(gè)目錄(或模塊)做記錄和標(biāo)識(shí),逐一閱讀,或者直接去讀你最感興趣的部分。
我讀源代碼喜歡自頂向下的方式,先把整體脈絡(luò)理清楚,然后按照模塊去閱讀代碼,把類和類、函數(shù)和函數(shù)之間的調(diào)用關(guān)系記錄下來(lái),如果可以進(jìn)行逆向工程,用類似 Intelli IDEA 這樣的工具把代碼之間的調(diào)用關(guān)系用 Diagrams 展現(xiàn)出來(lái),閱讀會(huì)更加直觀一些,不同的語(yǔ)言有不同的工具可以選擇。
另外,閱讀 test case 同樣能幫助你理解作者的代碼設(shè)計(jì)意圖。正常情況下,測(cè)試用例都是從文檔和設(shè)計(jì)衍生出來(lái)的,而不是完成了代碼再寫 test case。閱讀測(cè)試用例,可以讓你更清晰的知道對(duì)應(yīng)的類和函數(shù)想要做什么事情。
閱讀源代碼需要順手的工具,我自己喜歡用 Vim,配合 NERDTree、CtrlP、ctags、taglist 等插件,Vim 可以成為一款優(yōu)秀的代碼瀏覽工具,而且非常輕量級(jí)。你可以在終端里用命令迅速打開(kāi)、關(guān)閉、查找和索引程序,并進(jìn)行有效的關(guān)聯(lián)跳轉(zhuǎn)(靜態(tài)代碼)。如果你不習(xí)慣,也可以用 Sublime Text,Atom 等工具。當(dāng)然,如果你要進(jìn)行調(diào)試和跟蹤,那***使用相關(guān)程序棧的 IDE 工具,比如 Eclipse、Jet Brain 系列工具,Xcode 等等,這樣你可以在 debug 狀態(tài)跟蹤所有的函數(shù)調(diào)用和變量參數(shù)在執(zhí)行時(shí)間線上的變化。
如果你喜歡 Vim,可以看下這篇「Vim 8.0,姍姍來(lái)遲」。
重復(fù)一句,工具和方法永遠(yuǎn)不是最重要的,去讀,并在遇到困難的時(shí)候,看不明白的時(shí)候,咬牙堅(jiān)持下去,抽絲剝繭,逐個(gè)擊破。最終,你會(huì)在冰冷黑暗的二進(jìn)制世界里面看到一張地圖,找到一座燈塔,然后去解釋和還原這個(gè)底層世界里每一個(gè)細(xì)微方面的語(yǔ)義,重建出高層次的抽象概念和關(guān)系。
***推薦兩本書,《Docker 源碼分析》和《Go 語(yǔ)言學(xué)習(xí)筆記》,這兩本書,都是關(guān)于 Go 語(yǔ)言的,可以說(shuō)是源代碼閱讀的典范,有興趣可以找來(lái)讀讀。