代碼閱讀的姿勢

面向?qū)ο缶幊?/div>

眾里尋他千百度粪薛,驀然回首悴了,那人卻在,燈火闌珊處违寿。

一般地湃交,在一個程序員的日常工作之中,絕大多數(shù)時間都是在「閱讀代碼」陨界,而不是在「寫代碼」巡揍。但是菌瘪,閱讀代碼往往是一件很枯燥的事情,尤其當遇到了一個不漂亮的設(shè)計糜工,反抗的心理往往更加強烈捌木。

事實上,變換一下習(xí)慣澈圈、思路和方法瞬女,代碼閱讀其實是一個很享受的過程努潘。閱讀代碼的模式,實踐和習(xí)慣疯坤,集大成者莫過于希臘作者Diomidis Spinellis的經(jīng)典之作:Code Reading, The Open Source Perspective.压怠。本文從另外一個視角出發(fā),談?wù)勎易约洪喿x代碼的一些習(xí)慣洋闽,期待找到更多知音的共鳴突梦。

工欲善其事宫患,必先利其器

首先,閱讀代碼之前先準備好一個稱心如意的工具箱虚汛,包括IDE, UML卷哩,Mind Maping等工具属拾。我主要使用的編程語言包括C++, Scala, Java, Ruby;對于Scala, Java, Ruby編程尊浓,我更偏向使用JetBrain公司的產(chǎn)品栋齿;而對于C++編程,我依然還在使用Eclipse基协,因為Clion的特性還沒有讓我滿意堡掏。

其次,高效地使用快捷鍵鹅龄,這是一個良好的代碼閱讀習(xí)慣扮休,它極大地提高了代碼閱讀的效率和質(zhì)量。例如蜗搔,查看類層次關(guān)系八堡,函數(shù)調(diào)用鏈兄渺,方法引用點等等挂谍。

拔掉鼠標,減低對鼠標的依賴炼绘。當發(fā)現(xiàn)沒有鼠標而導(dǎo)致工作無法進行下去時俺亮,嘗試尋找對應(yīng)的快捷鍵驮捍。通過日常的點滴積累厌漂,工作效率必然能夠得到成倍的提高苇倡。

力行而后知之真

閱讀代碼一種常見的反模式就是「通過Debug的方式來閱讀代碼」旨椒。作者不推薦這種代碼閱讀的方式堵漱,其一勤庐,因為運行時線程間的切換很容易導(dǎo)致方向的迷失愉镰;其二,了解代碼調(diào)用棧對于理解系統(tǒng)行為并非見得有效录择,因為其包含太多實現(xiàn)細節(jié)隘竭,不易發(fā)現(xiàn)問題的本質(zhì)讼渊。

但在閱讀代碼之前,有幾件事情是必須做的弧圆。其一搔预,手動地構(gòu)建一次工程拯田,并運行測試用例甩十;其二,親自動手寫幾個Demo感受一下鸭轮。

先將工程跑起來窃爷,目的不是為了Debug代碼按厘,而是在于了解工程構(gòu)建的方式,及其認識系統(tǒng)的基本結(jié)構(gòu)卿堂,并體會系統(tǒng)的使用方式草描。

如果條件允許策严,可以嘗試使用ATDD的方式享钞,發(fā)現(xiàn)和挖掘系統(tǒng)的行為栗竖。通過這個過程,將自己當成一個客戶狐肢,思考系統(tǒng)的行為沥曹,這是理解系統(tǒng)最重要的基石妓美。

發(fā)現(xiàn)領(lǐng)域模型

發(fā)現(xiàn)「領(lǐng)域模型」是閱讀代碼最重要的一個目標壶栋,因為領(lǐng)域模型是系統(tǒng)的靈魂所在。通過代碼閱讀琉兜,找到系統(tǒng)本質(zhì)的模型豌蟋,并通過自己的模式表達出來梧疲,你才能真正地Hold住了系統(tǒng),否則一切都是空談擂找。

首要的任務(wù)贯涎,就是找到系統(tǒng)的邊界塘雳,并能夠以「抽象的思維」思考外部系統(tǒng)的行為特征败明。其次妻顶,尋找系統(tǒng)潛在的讳嘱,并能表達系統(tǒng)的重要概念酿愧,及其它們之間的關(guān)聯(lián)關(guān)系嬉挡。

細節(jié)是魔鬼

糾結(jié)于細節(jié)庞钢,將導(dǎo)致代碼閱讀代碼的效率和質(zhì)量大大折扣焊夸。例如仁连,日志打印饭冬,解決Bug的補丁實現(xiàn)昌抠,某版本分支的兼容方案,某變態(tài)用戶需求的錘子代碼等等裁厅。

閱讀代碼的一個常見的反模式就是「給代碼做批注」执虹。這是一個高耗低效唠梨,投入產(chǎn)出比極低的實踐袋励。越是優(yōu)雅的系統(tǒng),注釋越少当叭;越是復(fù)雜的系統(tǒng)茬故,再多的注釋也是于事無補。

我有一個代碼閱讀的習(xí)慣蚁鳖,為代碼閱讀建立一個單獨的code-reading分支磺芭,一邊閱讀代碼,一邊刪除這些無關(guān)的代碼醉箕。

$ git checkout -b code-reading

刪除這些噪聲后钾腺,你會發(fā)現(xiàn)系統(tǒng)根本沒有想象之中那么復(fù)雜。事實上讥裤,系統(tǒng)的復(fù)雜性,往往都是之前不成熟的設(shè)計和實現(xiàn)導(dǎo)致的額外復(fù)雜度剧辐。

適可而止

閱讀代碼的一個常見的反模式就是「一根筋走到底,不到黃河絕不死心」。程序員都擁有一顆好奇心鳄梅,總是對不清楚的事情感興趣冤狡。例如,消息是怎么發(fā)送出去的坦胶?任務(wù)調(diào)度工作原理是什么岖圈?數(shù)據(jù)存儲怎么做到的等等导匣;雖然這種勇氣值得贊揚,但在代碼閱讀時絕對不值得鼓勵渠牲。

還有另外一個常見的反模式就是「追蹤函數(shù)調(diào)用棧」谚咬。這是一個極度枯燥的過程背苦,常常導(dǎo)致思維的僵化;因為你永遠活在作者的陰影下,完全沒有自我。

我個人閱讀代碼的時候,函數(shù)調(diào)用棧深度絕不超過3,然后使用抽象的思維方式思考底層的調(diào)用。因為我發(fā)現(xiàn),隨著年齡的增長,曾今值得驕傲的記憶力誉己,現(xiàn)在逐漸地變成自己的短板。當我嘗試追蹤過深的調(diào)用棧之后坪蚁,之前的閱讀信息完全地消失記憶了。

也就是說男摧,我更習(xí)慣于「廣度遍歷」奏司,而不習(xí)慣于「深度遍歷」的閱讀方式。這樣,我才能找到系統(tǒng)隱晦存在的「分層概念」齿桃,并理順系統(tǒng)的結(jié)構(gòu)报破。

發(fā)現(xiàn)她的美

三人行,必有我?guī)熝伞T诖a閱讀代碼時柳琢,當發(fā)現(xiàn)好的設(shè)計,包括實現(xiàn)模式秦爆,習(xí)慣用法等望门,千萬不要錯過厨剪;否則過上一段時間丽惶,這次代碼閱讀對你來說就沒有什么價值了抡秆。

當我發(fā)現(xiàn)一個好的設(shè)計時着撩,我會嘗試使用類圖,狀態(tài)機奋救,時序圖等方式來表達設(shè)計演侯;如果發(fā)現(xiàn)潛在的不足,將自己的想法補充進去背亥,將更加完美秒际。

例如悬赏,當我閱讀Hamcrest時,嘗試畫畫類圖娄徊,并體會它們之間關(guān)系闽颇,感受一下設(shè)計的美感,也是受益頗多的寄锐。

Hamcrest匹配器

嘗試重構(gòu)

因為這是一次代碼閱讀的過程兵多,不會因為重構(gòu)帶來潛在風(fēng)險的問題。在一些復(fù)雜的邏輯橄仆,通過重構(gòu)的等價變換可以將其變得更加明晰剩膘,直觀。

對于一個巨函數(shù)盆顾,我常常會提取出一個抽象的代碼層次怠褐,以便發(fā)現(xiàn)它潛在的本質(zhì)邏輯。例如您宪,這是一個ArrayBuffer的實現(xiàn)奈懒,當需要在尾部添加一個元素時,既有的設(shè)計是這樣子的蚕涤。

def +=(elem: A): this.type = {
  if (size + 1 > array.length) {
    var newSize: Long = array.length
    while (n > newSize)
      newSize *= 2
    newSize = math.min(newSize, Int.MaxValue).toInt
  
    val newArray = new Array[AnyRef](newSize)
    System.arraycopy(array, 0, newArray, 0, size)
    array = newArray
  }
  array(size) = elem.asInstanceOf[AnyRef]
  size += 1
  this
}

這段代碼給閱讀造成了極大的障礙筐赔,我會通過快速的函數(shù)提取,發(fā)現(xiàn)邏輯的主干揖铜。

def +=(elem: A): this.type = {
  if (atCapacity)
    grow()
  addElement(elem)
}

至于atCapacity, grow, addElement是怎么實現(xiàn)的茴丰,壓根不用關(guān)心,因為我已經(jīng)達到閱讀代碼的效果了天吓。

形式化

當閱讀代碼時贿肩,有部分人習(xí)慣畫程序的「流程圖」。相反龄寞,我?guī)缀鯊膩聿粫嫛噶鞒虉D」汰规,因為流程圖反映了太多的實現(xiàn)細節(jié),而不能深刻地反映算法的本質(zhì)物邑。

我更傾向于使用「形式化」的方式來描述問題溜哮。它擁有數(shù)學(xué)的美感,簡潔的表達方式色解,及其高度抽象的思維茂嗓,對挖掘問題本質(zhì)極其關(guān)鍵。

例如科阎,對于FizzBuzzWhizz的問題述吸,相對于冗長的文字描述,流程圖等方式锣笨,形式化的方式將更加簡單蝌矛,并富有表達力道批。

3, 5, 7為輸入,形式化后描述后入撒,可清晰地挖掘出問題的本質(zhì)所在隆豹。

r1: times(3) => Fizz || 
    times(5) => Buzz ||
    times(7) => Whizz

r2: times(3) && times(5) && times(7) => FizzBuzzWhizz ||
    times(3) && times(5) => FizzBuzz  ||
    times(3) && times(7) => FizzWhizz ||
    times(5) && times(7) => BuzzWhizz

r3: contains(3) => Fizz

rd: others => string of others

spec: r3 || r2 || r1 || rd

實例化

實例化是認識問題的一種重要方法,當邏輯非常復(fù)雜時茅逮,一個簡單例子往往使自己豁然開朗噪伊。在理想的情況下,實例化可以做成自動化的測試用例氮唯,并以此描述系統(tǒng)的行為。

如果存在某個算法和實現(xiàn)都相當復(fù)雜時姨伟,也可以通過實例化探究算法的工作原理惩琉,這對于理解問題本身大有益處。

Spark中劃分DAG算法為例夺荒。假設(shè)GFinalRDD瞒渠,從后往前按照RDD的依賴關(guān)系,依次識別出各個Stage的起始邊界技扼。

Stage劃分算法
  • Stage 3的劃分:

    1. GB之間是Narrow Dependency伍玖,規(guī)約為同一Stage(3);
    2. BA之間是Wide DependencyA為新的FinalRDD剿吻,遞歸調(diào)用此過程窍箍;
    3. GF之間是Wide DependencyF為新的FinalRDD丽旅,遞歸調(diào)用此過程椰棘;
  • Stage 1的劃分

    1. A沒有父親RDDStage(1)劃分結(jié)束榄笙。特殊地Stage(1)僅包含RDD A邪狞;
  • Stage 2的劃分:

    1. RDD之間的關(guān)系都為Narrow Dependency,規(guī)約為同一個Stage(2);
    2. 直至RDD C, E茅撞,因沒有父親RDD帆卓,Stage(2)劃分結(jié)束;

最終米丘,形成了Stage的依賴關(guān)系剑令,依次提交Stage(TaskSet)TaskScheduler進行調(diào)度執(zhí)行。

獨樂樂不如眾樂樂

與他人分享你的經(jīng)驗蠕蚜,也許可以找到更多的啟發(fā)尚洽;尤其對于熟知該領(lǐng)域的人溝通,如果是Owner就更好了靶累,更能得到意外的驚喜和收獲腺毫。

也可以通過各種渠道癣疟,收集他人的經(jīng)驗,并結(jié)合自己的思考潮酒,推敲出自己的理解睛挚,如此才能將知識放入自己的囊中。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末急黎,一起剝皮案震驚了整個濱河市扎狱,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌勃教,老刑警劉巖淤击,帶你破解...
    沈念sama閱讀 206,126評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異故源,居然都是意外死亡污抬,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評論 2 382
  • 文/潘曉璐 我一進店門绳军,熙熙樓的掌柜王于貴愁眉苦臉地迎上來印机,“玉大人,你說我怎么就攤上這事门驾∩淙” “怎么了?”我有些...
    開封第一講書人閱讀 152,445評論 0 341
  • 文/不壞的土叔 我叫張陵奶是,是天一觀的道長楣责。 經(jīng)常有香客問我,道長聂沙,這世上最難降的妖魔是什么腐魂? 我笑而不...
    開封第一講書人閱讀 55,185評論 1 278
  • 正文 為了忘掉前任,我火速辦了婚禮逐纬,結(jié)果婚禮上蛔屹,老公的妹妹穿的比我還像新娘。我一直安慰自己豁生,他們只是感情好兔毒,可當我...
    茶點故事閱讀 64,178評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著甸箱,像睡著了一般育叁。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上芍殖,一...
    開封第一講書人閱讀 48,970評論 1 284
  • 那天豪嗽,我揣著相機與錄音,去河邊找鬼。 笑死龟梦,一個胖子當著我的面吹牛隐锭,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播计贰,決...
    沈念sama閱讀 38,276評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼钦睡,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了躁倒?” 一聲冷哼從身側(cè)響起荞怒,我...
    開封第一講書人閱讀 36,927評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎秧秉,沒想到半個月后褐桌,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,400評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡象迎,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,883評論 2 323
  • 正文 我和宋清朗相戀三年撩嚼,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片挖帘。...
    茶點故事閱讀 37,997評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖恋技,靈堂內(nèi)的尸體忽然破棺而出拇舀,到底是詐尸還是另有隱情,我是刑警寧澤蜻底,帶...
    沈念sama閱讀 33,646評論 4 322
  • 正文 年R本政府宣布骄崩,位于F島的核電站,受9級特大地震影響薄辅,放射性物質(zhì)發(fā)生泄漏要拂。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,213評論 3 307
  • 文/蒙蒙 一站楚、第九天 我趴在偏房一處隱蔽的房頂上張望脱惰。 院中可真熱鬧,春花似錦牌芋、人聲如沸战授。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,204評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽蔚润。三九已至,卻和暖如春尺栖,著一層夾襖步出監(jiān)牢的瞬間嫡纠,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,423評論 1 260
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留除盏,地道東北人叉橱。 一個月前我還...
    沈念sama閱讀 45,423評論 2 352
  • 正文 我出身青樓,卻偏偏與公主長得像痴颊,于是被迫代替她去往敵國和親赏迟。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,722評論 2 345

推薦閱讀更多精彩內(nèi)容