自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

我是怎么讀代碼的

原創(chuàng) 精選
開發(fā)
讀代碼很有趣,但要讀通讀懂也很費(fèi)功夫。本文是我在日常讀代碼中積累的一點(diǎn)心得,分享出來,希望能與大家產(chǎn)生共鳴。

作為一名程序員,總有一些時(shí)候,會(huì)對(duì)自己所做的重復(fù)性的工作感到厭倦,也會(huì)羨慕明星項(xiàng)目做得熱火朝天 Star 數(shù)蹭蹭上漲。而讀代碼,則是緩解焦慮的良方。

每當(dāng)讀懂軟件的精彩設(shè)計(jì),贊嘆優(yōu)美整潔的代碼,甚至發(fā)現(xiàn)藏在注釋中的彩蛋時(shí),都好像在不同的時(shí)空與作者產(chǎn)生了交叉,暢快地聊了會(huì)兒天。

讀代碼很有趣,但要讀通讀懂也很費(fèi)功夫。本文是我在日常讀代碼中積累的一點(diǎn)心得,分享出來,希望能與大家產(chǎn)生共鳴。

1. 尋找一位好老師

優(yōu)秀的項(xiàng)目就像一位好老師,我們可以從它身上全方位地學(xué)到各種領(lǐng)域知識(shí)。

不過在開始讀代碼之前, 最大的問題就是:怎么樣才能找到合適的代碼項(xiàng)目?

Star 數(shù)高的項(xiàng)目更優(yōu)秀嗎?從某些角度講的確是的,但是在 gitstar-ranking 上, 4k 個(gè) Star 的 Repo 只能排到第 5000 名,而至少有 50k 個(gè) Star 才有可能排進(jìn)前一百。光看 Star 數(shù),這項(xiàng)目太多了根本選不出來啊。

在我看來,抓住如下幾個(gè)方向,一般就能篩選到合適的項(xiàng)目了:

興趣使然

首先就是一定要選自己感興趣領(lǐng)域的項(xiàng)目。

不少代碼片段都是比較枯燥而難以閱讀的(比如“飛一般”的位操作,為提升性能而莫名其妙的語(yǔ)句,或是包含了大量隱含知識(shí)等等),只有自己感興趣,才會(huì)有讀下去的意愿和動(dòng)力,才能在其中發(fā)現(xiàn)樂趣。

有時(shí)候我們開始讀代碼的契機(jī)就單純只是在工作當(dāng)中用到了,想要進(jìn)一步了解其原理和設(shè)計(jì),這是一個(gè)不錯(cuò)的起點(diǎn)。想必很多同學(xué)第一次看源碼都是因?yàn)橐粚訉幼返搅?Spring 的 Class 里面去了吧。

有的時(shí)候可能就是覺得某項(xiàng)技術(shù)很神奇,像魔法一樣,越是猜不透,就越想了解它是怎么“施法”的。

總之一旦有了興趣,就會(huì)很想進(jìn)一步去了解它。不過,如果讀到一半又失去了興趣,也請(qǐng)大膽放棄它。失去了這一片草叢,還有整個(gè)樹林等著我們?nèi)ヌ剿鳌?/p>

經(jīng)典且被大量使用

經(jīng)典的、擁有大量用戶的項(xiàng)目,經(jīng)歷了時(shí)間的考驗(yàn),不斷地迭代,通常在設(shè)計(jì)上都有很多出眾之處。

經(jīng)典項(xiàng)目的維護(hù)者一般都是非常資深的工程師,并且也都會(huì)有大公司贊助,確保了代碼的高質(zhì)量。這類項(xiàng)目在閱讀的過程中能學(xué)到很多知識(shí),包括架構(gòu)抽象、性能優(yōu)化、工程化等等。

比較常見的典型的項(xiàng)目有如:Go、Kubernetes、MySQL 等等。

合適的規(guī)模

代碼量太過龐大的項(xiàng)目,有時(shí)雖然很出名,但難免令人生畏。實(shí)際上可以找到很多行數(shù)不多,但依然精彩的代碼庫(kù)。

首先就是各種語(yǔ)言的標(biāo)準(zhǔn)庫(kù),比如 Java 的 Stream、Lock 的實(shí)現(xiàn)等。另外也有不少開源的小而美項(xiàng)目,比如 redis、leveldb 等。甚至,許多經(jīng)典大學(xué)課程里面的 Lab 也隱藏著優(yōu)秀的代碼,比如 xv6。

總之,這一類代碼可能幾天或幾周就能大致看完主干,特別適合學(xué)習(xí)設(shè)計(jì)思想。

2. 先看文檔

選定了項(xiàng)目,我們就差不多能對(duì)它有一些淺嘗輒止的了解了。

這時(shí)候,先不要直接 Clone 代碼。代碼完整地包含了所有知識(shí),但也將細(xì)節(jié)毫無保留地暴露出來,直接進(jìn)到代碼里面很容易迷失方向。

對(duì)于事物的理解,從全局到部分,從抽象到細(xì)節(jié)才是一個(gè)比較容易讓人接受的過程。

成熟的項(xiàng)目通常會(huì)有比較詳盡的文檔,文檔一般分為兩類:給用戶看的使用文檔,和給貢獻(xiàn)者看的開發(fā)文檔。

了解概覽

通過閱讀使用文檔我們能快速地了解到項(xiàng)目創(chuàng)立的目的、解決了哪些問題,以及從用戶的視角看該軟件是什么樣子的。除了看 overview,我也會(huì)大致關(guān)注配置,通過必填配置可以進(jìn)一步了解軟件的依賴和外部特性。

以 TiDB 為例,它的使用文檔截圖如下:

圖片

從左側(cè)邊欄能了解到使用文檔的結(jié)構(gòu)包括了簡(jiǎn)介、部署、配置、參考等部分。這些部分都是使用者最關(guān)心的內(nèi)容。

架構(gòu)和模塊

優(yōu)秀的開發(fā)文檔一定會(huì)包括整個(gè)軟件的架構(gòu)模型,和關(guān)鍵模塊的設(shè)計(jì)。

通過閱讀架構(gòu)圖和高層設(shè)計(jì),軟件的原理以及解決問題的思路一目了然。對(duì)于有一定經(jīng)驗(yàn)的讀者甚至可能看到架構(gòu)設(shè)計(jì)后就已經(jīng)大概知道軟件的工作流程了。

圖片

上圖是 TiDB 開發(fā)文檔截圖,我們發(fā)現(xiàn)它不僅包含了架構(gòu)設(shè)計(jì),還事無巨細(xì)的告訴讀者如何啟動(dòng)代碼、怎樣貢獻(xiàn)、詳細(xì)的設(shè)計(jì)流程等等。除了架構(gòu)設(shè)計(jì),比較完善的開發(fā)文檔也會(huì)包含關(guān)鍵模塊的信息。關(guān)鍵模塊可能會(huì)涉及核心邏輯的設(shè)計(jì)和數(shù)據(jù)結(jié)構(gòu),以及邊界處的契約和交互方式等。對(duì)于 Go 語(yǔ)言這種持續(xù)演進(jìn)的開源編程語(yǔ)言,甚至專門建立了一個(gè) Proposal 倉(cāng)庫(kù)來追蹤各種提案的設(shè)計(jì)、討論、代碼以及發(fā)布的情況,比如等了 10 年的泛型提案:

圖片

搞明白了架構(gòu)模型和關(guān)鍵模塊,真正打開源碼時(shí)就能將包、文件名、接口等包含的知識(shí)與整體結(jié)構(gòu)相互映射,在腦中形成一張完整的圖。

其他的前置知識(shí)

有時(shí)候文檔的作者還會(huì)加上不少前置知識(shí),比如基于什么樣的算法,受到了哪些知識(shí)的啟發(fā)甚至是實(shí)現(xiàn)了哪篇論文的思想等等。

這些前置知識(shí),對(duì)我們的理解會(huì)大有幫助。我們可以通過學(xué)習(xí)這些知識(shí)來進(jìn)一步了解軟件的細(xì)節(jié)設(shè)計(jì)。

圖片

上圖是 etcd 的 github 頁(yè)面,在顯眼位置標(biāo)明了它采用 Raft 共識(shí)算法,并鏈接到 Raft 算法的主頁(yè),如果我們沒了解過 Raft,直接去讀 etcd 的代碼,很可能就對(duì)里面的選舉、日志復(fù)制等概念一知半解,這就好像在看沒有字幕的外文電影,精彩程度大打折扣。

3. 再讀代碼

看完了文檔,就可以開始看代碼了。為了防止在代碼中迷失方向,我們可以遵循幾條原則來閱讀:

從入口開始

雖說通過架構(gòu)模型以及包和文件劃分的關(guān)系,我們能大致確定哪些代碼是核心代碼,但從入口處開始看會(huì)更符合大腦的思考方式。

因?yàn)槿肟诖a的工作一般是先對(duì)各種模塊進(jìn)行初始化,然后調(diào)起主線程或者啟動(dòng)主服務(wù),這種明確順序的簡(jiǎn)單工作讓我們不會(huì)一開始就遇到困難,循序漸進(jìn)的過程更容易讓大腦產(chǎn)生獎(jiǎng)勵(lì)。

圖片

如圖所示是 kubelet 啟動(dòng)入口簡(jiǎn)化后的主線邏輯,非常清晰。以此為起點(diǎn)沉下去,就可以分三路去細(xì)看配置詳情、創(chuàng)建 kubelet 的詳情,以及啟動(dòng)的詳情。

抓住主線,從抽象到實(shí)現(xiàn)

主線就是從輸入是怎么樣一步步產(chǎn)生輸出的。在這一過程中,會(huì)涉及到多個(gè)模塊,每一個(gè)模塊又有自己的輸入和輸出。

當(dāng)我們順著函數(shù)調(diào)用、數(shù)據(jù)傳輸方向一步步向下時(shí),隨著抽象層次的不斷降低,涉及到越來越多的細(xì)節(jié),這個(gè)時(shí)候應(yīng)該及時(shí)折返,不要一路看到底,很容易迷失在里面。

良好的設(shè)計(jì)會(huì)有合理的抽象,根據(jù)不同的開發(fā)語(yǔ)言,我們可以通過查看包、接口、特性、公有方法列表、頭文件等等來快速獲取抽象信息,逐步地拼接出程序主線。搞清楚了主線,再逐步將抽象展開,閱讀具體實(shí)現(xiàn)代碼。

仍以 kubelet 為例,kubelet 作為負(fù)責(zé)整個(gè)節(jié)點(diǎn)運(yùn)轉(zhuǎn)的核心,工作多且雜。

圖片

但看它的代碼分包結(jié)構(gòu),非常清楚地將不同功能點(diǎn)劃分到不同目錄下,結(jié)合初始化邏輯,再進(jìn)一步深入到每個(gè)功能目錄內(nèi),又可以發(fā)現(xiàn) kubelet 的模塊設(shè)計(jì)遵循的是多個(gè) manager 圍繞著一個(gè)核心共同協(xié)作的模型。好的抽象,就像一顆洋蔥一樣,層層分明。

一邊閱讀一邊記錄

初識(shí)一個(gè)項(xiàng)目,對(duì)結(jié)構(gòu)和流程把握的不會(huì)太清楚,因此一邊讀一邊寫寫畫畫是很重要的。

有的時(shí)候跳轉(zhuǎn)次數(shù)比較多,前面看過的東西后面就忘記了,所以對(duì)關(guān)鍵路徑,記錄具體的函數(shù)名、模塊名,能幫助我們快速回溯到入口。

也有的時(shí)候遇到了需要拓展的知識(shí)盲區(qū),為了不打斷主線思路,可以先記錄下來,找其他時(shí)間再學(xué)習(xí)。

另外,遇到不直觀的、難以形成概念的代碼表達(dá),翻來覆去的看也看不懂,這個(gè)時(shí)候就需要畫個(gè)圖來幫助理解了。

一個(gè)典型的例子就是在學(xué)習(xí) B+Tree 的分裂、合并、上移下移的時(shí)候,全看代碼特別不直觀,想要理解這類內(nèi)容畫圖定有奇效:

圖片

必要時(shí)借助 debug

有一些代碼為了正確性、性能等考慮,其表述可能會(huì)讓人百思不得其解。人類的思維方式是偏向順序的,用軟件開發(fā)做類比就是,我們更容易理解 Happy Path,而忽視分支細(xì)節(jié)。

當(dāng)橫豎想不通某段代碼為什么要這么寫的時(shí)候,實(shí)際運(yùn)行一遍,加斷點(diǎn) Debug 一下可能就會(huì)發(fā)現(xiàn)真實(shí)的原因了。

一個(gè)有趣的例子是:在環(huán)形隊(duì)列中,判斷隊(duì)列是否為空需要看頭指針和尾指針是不是已經(jīng)重合,下圖的代碼來自一個(gè)無鎖環(huán)形隊(duì)列的判空實(shí)現(xiàn)。

圖片

道理上講,環(huán)形隊(duì)列入隊(duì) tail++,出隊(duì) head++,先有入隊(duì),才會(huì)有出隊(duì),所以 tail 一定比 head 大。那為什么上面代碼里,除了判斷 tail - head == 0 以外,還一定要加上當(dāng) tail < head 時(shí)也認(rèn)為空呢,這根本不可能發(fā)生啊?

實(shí)際的原因是,由于該環(huán)形隊(duì)列是無鎖的, tail 和 head 之間不保證任何同步,那么就可能由于調(diào)度因素,導(dǎo)致不同線程讀到不同時(shí)刻的值,結(jié)果 tail < head 就真的產(chǎn)生了。

想要搞清楚這種場(chǎng)景,最好的辦法就是真正執(zhí)行幾百萬(wàn)次測(cè)試,通過條件斷點(diǎn)讓代碼在發(fā)生 tail < head 時(shí)停住,再觀察內(nèi)存中的值來解釋。

4. 寫篇文章講講整個(gè)設(shè)計(jì)

代碼看個(gè)七七八八,差不多就對(duì)設(shè)計(jì)和實(shí)現(xiàn)都有一定的認(rèn)識(shí)了。這時(shí)候心里多少會(huì)有點(diǎn)沖動(dòng)想要把獲得的知識(shí)講出來。那么最好就是寫篇文章,寫文章可以對(duì)知識(shí)進(jìn)行梳理,在寫的過程中也會(huì)不斷加深印象。隨著文章的撰寫,作者的設(shè)計(jì)意圖亦會(huì)越來越清晰,對(duì)軟件的理解也會(huì)越來越深刻。

整理大綱

寫文章,目錄最關(guān)鍵。一篇文章是不是有邏輯性,結(jié)構(gòu)是不是清晰,全都在大綱的設(shè)計(jì)上。

既然我們的內(nèi)容是講軟件的設(shè)計(jì)與實(shí)現(xiàn),那么文章的大綱就可以按 Why - What - How 來展開:先告訴讀者為什么要設(shè)計(jì)該軟件,它解決了哪些問題。之后講述軟件的架構(gòu)模型、關(guān)鍵模塊以及主線流程。最后詳細(xì)地講解具體實(shí)現(xiàn)。

在寫文章之初,我們的知識(shí)還不夠深入和整體,可以先寫 what 和 how 的部分,加深理解之后,就能明白設(shè)計(jì)的 why 了。

?描述設(shè)計(jì)原理,通過畫圖幫助分析設(shè)計(jì)意圖

在介紹原理和實(shí)現(xiàn)的時(shí)候,相比于貼代碼,更好的方式是通過畫圖來表達(dá)。代碼的確能體現(xiàn)全部的設(shè)計(jì)細(xì)節(jié),但代碼更重要的任務(wù)是作為知識(shí)和硬件指令之間的橋梁。相反,如果我們用圖表的形式表達(dá)設(shè)計(jì)意圖,就會(huì)對(duì)人類更友好,更容易閱讀、理解和學(xué)習(xí)。畫圖本身也是一種加深理解,去粗取精的過程。

下圖是我讀了 leveldb 之后畫的 leveldb 存儲(chǔ)架構(gòu)圖:

圖片

作為存儲(chǔ)引擎,LSM Tree 的實(shí)現(xiàn)是 leveldb 的核心,leveldb 本身源碼已經(jīng)很清晰、簡(jiǎn)潔,但如果通過上面這樣一張圖來講述其 LSM Tree 的具體設(shè)計(jì),一定會(huì)比貼代碼要易懂得多。

想一想,為什么要這么設(shè)計(jì),好處在哪里?

當(dāng)我們能用圖表和文字來表達(dá)出軟件的完整設(shè)計(jì)后,我們對(duì)代碼的理解已經(jīng)比較透徹,甚至,讓我們自己來照著寫一個(gè)新的也不是不可能了。

這個(gè)時(shí)候,就應(yīng)該進(jìn)一步的思考,如果是我自己來解決問題,我會(huì)怎么做?我能比原作者做得更好嗎(通常不能)?

在思考為什么這么設(shè)計(jì)的時(shí)候,如果相關(guān)領(lǐng)域知識(shí)不充足,就會(huì)驅(qū)使我們?nèi)ゲ檎液芏鄥⒖假Y料,了解和借鑒別人看問題的角度。找資料的過程總有驚喜,如果能讀到一些非常深入淺出的文章,而后就會(huì)懷著敬佩之情,收藏、關(guān)注作者的博客,想想如果不是因?yàn)樽x了某段代碼,還真無緣遇到這些精彩的文章和優(yōu)秀的作者。

我在讀 Go 語(yǔ)言內(nèi)存管理代碼的時(shí)候,一開始搞懂了 tcmalloc 的原理和實(shí)現(xiàn),但對(duì)其所謂線程緩存、無鎖分配等等賣點(diǎn)理解不深刻。直到回過頭去讀了 CSAPP 動(dòng)態(tài)內(nèi)存分配的章節(jié),又結(jié)合 ptmalloc、jemalloc 的設(shè)計(jì),相互對(duì)比理解,這才更清晰的認(rèn)識(shí)了 tcmalloc 的設(shè)計(jì)決策。

經(jīng)過這一階段的思考并結(jié)合其他人的理解之后,我們就能清楚地意識(shí)到,軟件所面臨問題的限制條件是什么,作者這樣設(shè)計(jì)的好處有哪些。把這部分寫完,添加到文章的最開始,就比較完美了。

5. 講個(gè) Session,收獲 Extra Bonus

如果還有精力和興致,那不如把文章的內(nèi)容提取出來做個(gè) Session 講給大家,額外的付出能收獲額外的獎(jiǎng)賞。

有過做講師經(jīng)歷的同學(xué)肯定會(huì)知道,給別人講東西,收獲最大的不是聽眾,而是講師本人。想要輸出一小時(shí)的 Session,所花費(fèi)的準(zhǔn)備時(shí)間可能要十個(gè)小時(shí)。我們需要花費(fèi)數(shù)倍于講解的時(shí)間來完善素材,理清思路,準(zhǔn)備問題,甚至還包括思考可能會(huì)涉及到的拓展內(nèi)容。做這些工作在提升我們 session 質(zhì)量的同時(shí),無形中也不斷地強(qiáng)化了我們對(duì)相關(guān)知識(shí)的認(rèn)知。

梳理要點(diǎn),邏輯自洽

一個(gè) Session 成功的基礎(chǔ)在于能不能邏輯自洽。而邏輯自洽的前提就是關(guān)鍵要點(diǎn)必須清晰,并且前后可以呼應(yīng)。

上一節(jié)提到的文章,正好就是 Session 材料的源泉,因此我會(huì)反復(fù)遍歷整篇文章,期望從中抽取所需的內(nèi)容。這個(gè)過程往往伴隨著不斷地發(fā)現(xiàn)文章的內(nèi)容缺失、邏輯不通之處,這時(shí)文章就得到了進(jìn)一步的改善。所以經(jīng)常發(fā)現(xiàn),當(dāng)整個(gè) Slide 完整的順下來后,不僅成就感爆棚,文章也豐滿了,理解還更深刻了。

去粗取精,鍛煉表達(dá)

相比寫文章,講 Session 要我們進(jìn)一步的去除細(xì)節(jié),只保留最核心的思想,這本身是對(duì)抽象能力的一種鍛煉。

另外,自己了解清楚,和能給別人講清楚是完全不同的兩種概念。如何能把核心知識(shí)講給聽眾,并且能讓聽眾更容易的聽懂,需要仔細(xì)地思考語(yǔ)言的表達(dá)。每一次成功的 Session 都是對(duì)自己表達(dá)能力的一次提升。

表達(dá)上最常見的問題就是照著文字念。我個(gè)人喜歡通過減少 Slide 中文字的數(shù)量,來倒逼自己提升表達(dá)的邏輯性與連貫性??梢試L試思考,如果內(nèi)容只是一張圖,那么要怎么講清楚這張圖,用這種辦法訓(xùn)練表達(dá)能力。

揣摩聽眾感興趣的方向

考慮聽眾的感受也很重要,如果講的內(nèi)容大家不感興趣,不愛聽,或是晦澀難懂,跟不上節(jié)奏,就容易導(dǎo)致整個(gè) Session 反響寥寥,大家會(huì)覺得來聽你的 Session 浪費(fèi)了自己的時(shí)間。

所以不僅要能講清楚,還要揣摩聽眾感興趣的方向。合理的設(shè)置內(nèi)容,去除枯燥乏味而非關(guān)鍵性的東西,并且調(diào)整講解的順序,把易懂的、精彩的部分穿插放置,這樣就可以不斷地激發(fā)聽眾的興趣。

最后,線下組織的效果要比線上視頻講解好得多。在線下聽眾的注意力更集中,互動(dòng)效果好,演講者也更容易通過聽眾的表情、神態(tài)來判斷是否需要調(diào)整內(nèi)容和速度。如果因?yàn)闂l件限制一定要做視頻 Session,那么可能需要經(jīng)常停頓下來問些問題,或是主動(dòng)的尋求反饋。

6. 結(jié)語(yǔ)

本文是我日常讀代碼的一點(diǎn)經(jīng)驗(yàn),總結(jié)下來,就是要:

  • 仔細(xì)地選擇學(xué)習(xí)的項(xiàng)目;
  • 先通過文檔了解全景,再逐步深入代碼;
  • 找對(duì)抽象和邊界,能幫助我們建立思考模型;
  • 寫篇文章講述代碼的設(shè)計(jì),是深入理解代碼的好辦法;
  • 自己學(xué)會(huì)了還不夠,能清楚地講給別人才是真正的掌握。

最后祝愿所有讀者都能從代碼中獲得最大的樂趣。

原文鏈接:??https://mp.weixin.qq.com/s?__biz=MjM5MjY3OTgwMA==&mid=2652483509&idx=1&sn=38f3849ec43c18e208851c4311e8b5bf&chksm=bd4f9da28a3814b4a23a1864aa344f372968bf6578176dbe6b12be5e5a330cadf7fa01775146&mpshare=1&scene=23&srcid=1019sHhb3idFjd7yXU423Coc&sharer_sharetime=1666148297055&sharer_shareid=c01707d0f17ed01b69a77c2668e647eb#rd??


責(zé)任編輯:趙寧寧 來源: 51CTO
相關(guān)推薦

2021-07-02 07:06:20

調(diào)試代碼crash

2019-02-21 23:36:09

源碼框架讀源碼

2021-04-27 08:25:52

MVCC數(shù)據(jù)MySQL

2021-10-18 11:05:51

SQL源代碼AddUpdate

2020-12-16 08:33:58

Excel數(shù)據(jù)分析FineBI

2021-02-02 10:53:16

Python編程開發(fā)

2019-05-28 13:50:27

MySQL幻讀數(shù)據(jù)庫(kù)

2012-05-24 14:58:55

開源代碼

2020-10-20 10:14:01

JVM內(nèi)存模型

2022-10-27 06:48:23

sourcemap源碼Element

2021-10-20 09:20:40

手機(jī)定位互聯(lián)網(wǎng)位置服務(wù)

2013-06-08 14:12:13

程序員招聘

2013-05-02 09:36:44

代碼項(xiàng)目

2019-11-25 09:29:42

團(tuán)隊(duì)項(xiàng)目經(jīng)驗(yàn)

2015-01-28 13:10:55

2024-02-21 23:03:56

代碼系統(tǒng)

2018-02-25 11:00:34

代碼開發(fā)程序員

2012-08-01 09:23:31

代碼

2021-06-15 11:16:49

代碼耦合開發(fā)

2020-10-19 09:51:18

MYSQL知識(shí)數(shù)據(jù)庫(kù)
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)