閱讀大型開(kāi)源軟件的四個(gè)技巧
最近的一段時(shí)間里,我在研究 Android 配套工具和 Android Studio 相關(guān)的實(shí)現(xiàn),以及它們?nèi)绾闻浜贤瓿梢粋€(gè) APK 的構(gòu)建。因?yàn)檎麄€(gè)系統(tǒng)各個(gè)模塊之間的關(guān)系過(guò)于復(fù)雜,除此,不同模塊之間也包含了大量的代碼 —— 無(wú)論是從行數(shù)上,還是從函數(shù)長(zhǎng)度上來(lái)說(shuō)。
從總體的思路上來(lái)說(shuō),在進(jìn)入代碼閱讀之前,我們需要:
- 理解代碼背后的業(yè)務(wù)流程
- 理解架構(gòu)設(shè)計(jì)的思想
從而我們才能理解主流程(脈絡(luò))。針對(duì)于此,我們會(huì)發(fā)現(xiàn)一些不同的模式:
- 借鑒他人。從他人的學(xué)習(xí)筆記中,理解整體的思路和過(guò)程。如 Android APK 的構(gòu)建,Android 資源如何優(yōu)化,從中理清代碼閱讀的思路。
- 源碼學(xué)習(xí)。
- 借助測(cè)試調(diào)試。
- fork 主流程。
它們并不是互相獨(dú)立的,往往是結(jié)合一起使用的。
借鑒他人
這種模式,可以實(shí)現(xiàn)快速地學(xué)習(xí)。它存在的一些明顯的缺點(diǎn)是:
- 學(xué)到的東西是二手加工過(guò)的。
- 部分的代碼可能與真實(shí)的情形脫節(jié)。
所以,它適用于你想快速了解某一部分的功能,從而了解全貌,隨后我們就可以深入某一部分進(jìn)行了解。
在這種模式之下,我推薦:通過(guò)購(gòu)買、閱讀書(shū)籍的方式來(lái)學(xué)習(xí)。如果能買到書(shū)便是一件幸運(yùn)的事,因?yàn)樗呀?jīng)經(jīng)過(guò)了系統(tǒng)性的加工。唯一的問(wèn)題可能是上面的代碼有些老舊。但是,它更加的系統(tǒng)化、完整,方便我們理解,并減少搜索資料的成本。
為什么不是網(wǎng)上找資料?:
- 找資料需要投入時(shí)間成本
- 資料不一定詳細(xì)
如果你能直接找到詳細(xì)的資料,畢竟花費(fèi)的時(shí)間足夠短的話,那么也是沒(méi)問(wèn)題的。
源碼學(xué)習(xí)
源碼學(xué)習(xí)是一個(gè)需要花費(fèi)大量時(shí)間和精力的事情,除非萬(wàn)不得已,否則我也不想用這種方式。因?yàn)椋覀儠?huì)缺少大量的上下文,這些上下文可能導(dǎo)致我們理解出現(xiàn)一些誤差。
前期準(zhǔn)備:
- 合適的工具。最好是~~收費(fèi)的(🐶)~~能用上的。
- 合適的存儲(chǔ)空間。像 Android 這樣的系統(tǒng),clone 下來(lái)就要 120G,編譯的話,估計(jì)得達(dá)到 200G 吧;而像 Android Studio 的源碼,clone 下來(lái)也要 60G。
- 尋找閱讀的模式。
- 嘗試去構(gòu)建應(yīng)用。它不一定可行,但是如果可以的話,會(huì)節(jié)省你大量的時(shí)間。
源碼學(xué)習(xí)是一個(gè)非常重的學(xué)習(xí)模式。我們要花費(fèi)大量的時(shí)間:
- 在代碼間跳轉(zhuǎn)
- 梳理業(yè)務(wù)邏輯
所以,還有一些不錯(cuò)的犯懶的姿勢(shì):
通過(guò)書(shū)籍來(lái)加強(qiáng)。我一直覺(jué)得對(duì)于學(xué)習(xí)來(lái)說(shuō),閱讀書(shū)籍是最理想的方式。因?yàn)閷ふ屹Y料需要成本,而多數(shù)的書(shū)都會(huì)起到一個(gè)索引的目的。
尋找相似的輪子。一個(gè)有意思的技術(shù),必然有很多公司、很多人都研究過(guò)。他們都會(huì)嘗試去創(chuàng)造類似的輪子。唯一的問(wèn)題是,我們要學(xué)習(xí)到什么程度,如果只是理解的話,那么看看別人重復(fù)的輪子也是可以的;如果是為了深入的話,那么還得回過(guò)頭去看看源碼
借助測(cè)試調(diào)試
對(duì)于調(diào)試來(lái)說(shuō),我們還會(huì)面臨的一個(gè)挑戰(zhàn)是:諸如我這樣的入門(mén)級(jí) MBP 配置,對(duì)于大型系統(tǒng)來(lái)說(shuō)編譯根本不夠用。應(yīng)對(duì)這種問(wèn)題的一個(gè)比較良好的姿勢(shì)是:通過(guò) IDE 調(diào)試測(cè)試來(lái)完成對(duì)部分代碼的調(diào)試。(PS:這種方式也適用于業(yè)務(wù)代碼的開(kāi)發(fā))
如果我們可以在應(yīng)用的入口中創(chuàng)建某一模塊對(duì)應(yīng)的測(cè)試,那么我們就可以快速調(diào)試整個(gè)應(yīng)用了。
fork 主流程
對(duì)于我來(lái)說(shuō),我覺(jué)得只閱讀源碼是一種只為了解決一時(shí)問(wèn)題的方式。同時(shí),像我這樣的凡人,對(duì)于某些知識(shí)和內(nèi)容,只要不使用,我可能隔個(gè)十天半個(gè)月,我就忘光了(雖然我一直覺(jué)得這是一件好事)。
邊閱讀代碼,邊 fork 項(xiàng)目,我們還會(huì)有一些挑戰(zhàn):
- 語(yǔ)言的熟練度和模式。對(duì)于熟悉的語(yǔ)言來(lái)說(shuō),比如日常編寫(xiě)業(yè)務(wù)代碼的時(shí)候,我們并不需要理解于諸如類加載器、元編程、字節(jié)碼這一類的復(fù)雜模式。
- 新的框架、工具或語(yǔ)言的學(xué)習(xí)成本。比如,我在過(guò)程中就遇到需要理解和學(xué)習(xí) Gradle 插件的一些構(gòu)建機(jī)制。
- 代碼中大量的、無(wú)用的異常處理的代碼。
- 別人的代碼都是 💩。(PS:一個(gè)月后自己的代碼也是屎)
所以,還有一些模式:
- 劃分模塊邊界。尋找架構(gòu)圖,通過(guò)架構(gòu)圖來(lái)劃分模塊。
- 切片化運(yùn)行。一個(gè)模塊,一個(gè)模塊來(lái)理解整個(gè)系統(tǒng)。如 IDEA 插件的編寫(xiě)、IDEA 插件與 Gradle 如何交互,Gradle 插件的原理與編寫(xiě),Gradle 如何調(diào)用其它命令行工具,命令行工具的原理與編寫(xiě)。
- 通過(guò)測(cè)試運(yùn)行。如針對(duì)于 ApkAnalyser 這樣的工具,我們可以通過(guò)單元測(cè)試而非構(gòu)建一個(gè) CLI 的方式來(lái)運(yùn)行。
- 選擇另外一門(mén)語(yǔ)言。因?yàn)閯e人用 Java、Groovy、Kotlin 編寫(xiě)的應(yīng)用,如果你用 Rust、Go 再寫(xiě)一遍的話,那么你就能一次學(xué)到兩個(gè)東西了:一個(gè)是新的編程語(yǔ)言,一個(gè)是這個(gè)開(kāi)源項(xiàng)目的代碼。
README 輸出
為了方便我們查閱和其他/她人使用,我往往會(huì)把相關(guān)的內(nèi)容記錄到項(xiàng)目的 README 上。
- 相關(guān)的文檔資料
- 相似的開(kāi)源項(xiàng)目
- 過(guò)程中的內(nèi)容產(chǎn)出
- 代碼簡(jiǎn)要說(shuō)明
- ……
這樣一來(lái),其他/她人在學(xué)習(xí)的過(guò)程中還能 GET 到相似的思路。
結(jié)論
最后,簡(jiǎn)單做一些成本對(duì)比:
模式 | 成本 | 性價(jià)比 | 主要場(chǎng)景 |
---|---|---|---|
借鑒他人 | 低 | 高 | 學(xué)習(xí) |
閱讀源碼學(xué)習(xí) | 高 | 低 | 理解思想 |
fork 主流程 | 高 | 低 | 理解、模仿 |
借助測(cè)試調(diào)試 | 較高 | 中 | 理解、模仿 |
一些結(jié)合模式:
閱讀二手資料,根據(jù)二手資料理解主脈絡(luò)
編寫(xiě)主流程調(diào)用鏈,理解架構(gòu)設(shè)計(jì)理想
借助開(kāi)源軟件的測(cè)試調(diào)試,理解參數(shù)及流程
……
本文轉(zhuǎn)載自微信公眾號(hào)「phodal」,可以通過(guò)以下二維碼關(guān)注。轉(zhuǎn)載本文請(qǐng)聯(lián)系phodal公眾號(hào)。