版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領
文檔簡介
1、# 5. 理解源代碼在這一章中,只打算討論以命令式編程范型為主的語言,因為其他的編程范型的開源項目,筆者接觸太少了(期待各類達人多多補充)。# 5.1. 靜態(tài)理解閱讀一個開源項目的源代碼,通常都很容易。大多數(shù)開源項目的托管網(wǎng)站,都提供了無需下載,直接閱讀源代碼的功能,比較有趣的是,大家可以比較一下 sourceforge、google code以及github的查看源代碼的功能。這分別代表了老、中、青三代開源托管平臺,對于查看代碼的重視程度。# 5.1.1. 目錄結構好的開源項目,通常會選擇合理的目錄結構,來組織自己的代碼。而所謂合理,通常意味著遵循最常見的約定俗成。比如:|目錄名|含義|-|
2、-|conf/configure|各種配置文件|src/source|項目的源代碼|doc/document|項目文檔|test/unittest|單元測試|tools/utils|相關工具|lib|庫文件|app|應用相關的文件(在web項目中經(jīng)常出現(xiàn))|controllers|控制器,在遵循MVC模式的Web項目中,經(jīng)常出現(xiàn)|models|模型,在遵循MVC模式的Web項目中,經(jīng)常出現(xiàn)|views|視圖,在遵循MVC模式的Web項目中,經(jīng)常出現(xiàn)|db|數(shù)據(jù)庫相關文件|demo/example|相關示例代碼|misc|其他雜項|include|頭文件所在目錄,c/c+項目中常見|out/bu
3、ild|編譯結果輸出目錄|third_party/vender|第三方庫|install|安裝所需的相關文件|# 5.1.2. 包名與文件名在軟件體系中,包(Package)是一個很重要的概念,與模塊(Module)類似,但是又有所區(qū)別。一個項目,在初始設計時,就需要做模塊劃分,每一個大小合適的模塊,往往就可以作為一個開發(fā)工作單元,分配給某個開發(fā)者完成。當然,對于那種大型的、復雜的項目,還需對模塊做進一步的細分,比如:子模塊(Sub Module)。而包(Package),則往往具有一定的可重用性。我們可以認為,一個模塊,開源出去未必會有人來用。而一個設計良好的包,本身就可以作為一個開源項目,
4、放出去給被人使用。因此,從更加有利于軟件開發(fā)的協(xié)作的角度來說,合理的包命名,就變得非常重要。越是現(xiàn)代的開源項目,越是懂得不必一切從零開始搭建,所以,我們常常會發(fā)現(xiàn),一個開源項目,他們自己會開發(fā)一組Package,同時也依賴一批別人開發(fā)的Package。在靜態(tài)理解項目時,了解一個項目項目有哪些包,以及依賴哪些包,就非常重要。舉例之一:rails是一個著名的ruby開源項目。我們訪問它的github主頁(/rails/rails),就可以看到這個項目的源代碼結構。* 首先需要選擇查看某一個穩(wěn)定版本,比如3.2版 * /rails/rails/tree/3-2-stable* 然后閱讀install
5、.rb文件 * /rails/rails/blob/3-2-stable/install.rb* 我們可以看到的內容,主要包含兩大部分: * 編譯rails的依賴包:activesupport、activemodel、activerecord、activeresource、actionpack、actionmailer、railties。這些都是rails項目自己開發(fā)的包 * 然后再編譯rails本身* rails本身的包描述文件是:rails.gemspec * /rails/rails/blob/3-2-stable/rails.gemspec* 通過閱讀rails.gemspec,我們可
6、以了解到,rails這個項目,依賴的包還包括:bundler、sprockets-rails。 * 事實上,通過閱讀activesupport等一系列包的.gemspec文件,我們還會發(fā)現(xiàn)更多的外部依賴包。不同的項目,描述包文件,以及包依賴關系,有各種不同的格式。需要一一分別學習。這里就不再詳細解說了。在一個開源項目中,代碼當然是由一個一個的源代碼文件組成的。通過查看文件名,往往可以了解一個文件的大概內容。例如:* errors.rb,通常會與出錯處理有關* i18n.rb,通常會與國際化有關* logger.rb,通常會與日志有關* json目錄下的兩個文件decoding.rb和encod
7、ing.rb,自然是JSON格式的編解碼相關代碼通常,要迅速的辨認出一個文件名的含義,與領域知識大有關系。例如:http.rb,通常會是處理http協(xié)議相關。而request和response,則通常是網(wǎng)絡協(xié)議中的請求與響應相關的處理代碼。對于這些單詞的熟悉程度,決定了我們閱讀與理解代碼的迅捷程度。# 5.1.3. 類名、函數(shù)名與變量名java是一門很講究規(guī)范的語言,所以他的每一個類,就會對應一個同名的.java文件(內部類除外)。這使得我們尋找類所在的源文件,變得非常簡單。當然,這樣會造成源文件數(shù)量的增加,也許會有人不喜歡。不同的語言,對于命名有其自己的規(guī)范,我們可以做一個列表,來簡單列出這
8、些規(guī)范。|語言|包/命名空間命名|類命名|函數(shù)/方法命名|常量命名|變量命名|-|-|-|-|-|-|Java|domainname.package 全部都是小寫的單詞,以.區(qū)隔|ThisClassName 每個單詞都以大寫字母開頭|theMethodName 第一個單詞以小寫字母開頭|THE_VALUE 全部大寫,單詞以下劃線分隔|theValue 與函數(shù)名一致|C#|DomainName.Package 每個單詞都以大寫字母開頭,以.區(qū)隔|ThisClassName 每個單詞都以大寫字母開頭|TheMethondName 與類名一致|THE_VALUE 全部大寫,單詞以下劃線分隔|TheV
9、alue 與類名一致|PHP|domainname.package 全部都是小寫的單詞,以.區(qū)隔|ThisClassName 每個單詞都以大寫字母開頭|theMethodName 第一個單詞以小寫字母開頭|$THE_VALUE 全部大寫,單詞以下劃線分隔|$theValue 與函數(shù)名一致|C/C+|std:hex 全部小寫,以:區(qū)隔|CThisClassName 以大寫C開頭,后續(xù)是大寫字母開頭的單詞|TheMethodName 每個單詞以大寫字母開頭|nMAX_VALUE 特別會引入前綴的概念,例如:n代表整形、b代表布爾型、c代表字符型等等|nTheValue 與常量類似,單詞區(qū)分大小寫|
10、Delphi|MyUnit.Unit2 遵循Pascal命名法:一個名字里如果包含多個單詞,每個單詞的首字母都要大寫,以.區(qū)隔|TThisClassName 以大寫T開頭,后續(xù)是大寫字母開頭的單詞|TheMethodName 每個單詞以大寫字母開頭|castMaxValue 特別會引入前綴的概念,例如:i代表整形、b代表布爾型、c代表字符型等等,cast代表常量|iTheValue 與常量類似,單詞區(qū)分大小寫|Ruby|Module:SubModule 每個單詞以大寫開頭,以:區(qū)隔|ThisClassName 每個單詞都以大寫字母開頭|the_method_name 全小寫單詞,以下劃線分隔,
11、!?有特定的含義|MAX_VALUE 全大寫單詞,以下劃線分隔|the_value 全小寫單詞,以下劃線分隔|Python|mod_submod 全部小寫,以_區(qū)隔|ThisClassName 每個單詞都以大寫字母開頭|the_method_name/theMethodName 全小寫單詞,以下劃線分隔,也可以類似Java的命名,私有函數(shù)以雙下劃線開頭|MAX_VALUE 全大寫,以下劃線分隔|the_value 全小寫,以下劃線分隔|JavaScript|無|ThisClassName 每個單詞都以大寫字母開頭|thePrivateMethod/ThePublicMethod 私有函數(shù)小寫字
12、母開頭,公有函數(shù)以大寫字母開頭|MAX_VALUE 全大寫,下劃線分隔|theValue 小寫字母開頭,私有變量,加下劃線|這是一個非常粗略,掛一漏萬的表格,詳細的命名規(guī)范,請參考各種具體語言的命名規(guī)范文檔。# 5.1.4. 注釋與Readme很多時候,開源項目代碼里的注釋,會給你帶來誤導?;蛘邥屓瞬恢?,或者只是寫給自己看到TODO,或者代碼改了,注釋忘記改。種種原因,因此我強烈的不建議過于重視注釋。但是,在業(yè)內有一種流派,非常重視注釋,而且信奉從源代碼的注釋,就可以直接生成項目的開發(fā)文檔。比如JavaDoc這樣的東西,在java的開 源項目里,簡直用到泛濫,也造成了java的很多項目,
13、注釋數(shù)量比代碼的數(shù)量還要多。而且,格式規(guī)范,千篇一律(為了生成Document),真正有意義 的注釋內容,少之又少。純粹是干擾閱讀。很多時候,我都建議閱讀代碼,就真的去讀代碼。然后試著從類名、方法名、變量名中,大致“猜出”代碼的意義。然后再實際的將代碼運行起來,看看執(zhí)行過程中,這些代碼是如何工作的。總之,不到萬不得已,不要先看注釋。雖然我個人對于注釋,相當?shù)牟恢匾?,但是卻非常認同Readme的價值。令我倍感欣慰的是,Github的創(chuàng)立者,也完全贊成這一點。他們將一個 項目的首頁,直接規(guī)定為“代碼展示+Readme”,也就強迫所有在Github上安家的開源項目,將更多的精力,投注到Readme的
14、撰寫中去。這樣形 成的良性循環(huán),使得我們可以樂觀的預期:越來越多的開源軟件,將會越來越重視項目根目錄下的Readme文件的價值,將一個項目最為重要的內容,以最為精 煉的方式,在Readme中,以結構良好的方式,展現(xiàn)出來。因此,首先閱讀Readme,對于了解一個開源項目,是一個非常好的選擇。# 5.1.5. UML圖UML是一種軟件建模語言,全稱為:統(tǒng)一建模語言(UML,Unified Modeling Language)。非常巧合的是,在打算寫這一小節(jié)的今天,蔡學庸 發(fā)了兩條微博: 有時候我會一邊讀源碼,一邊將我的理解畫成圖。這是我最近讀源碼畫的圖,還沒有美化處理。 閱讀源代碼時,將代碼順手圖
15、像化,有助于我思考與記憶。位置、色彩、形狀,這些都是我將源代碼圖像化時會采用的手段。我非常期待,他能夠就這個問題,談到更多的心得。在我看來,在閱讀源代碼的時候,不斷記錄,在腦海里形成整個項目的全景圖像,是非常有幫助的。關于UML的定義,可以參考維基百科: UML(/wiki/UML),以下引用一段: 統(tǒng)一建模語言(UML,Unified Modeling Language)是非專利的第三代建模和規(guī)約語言。UML是一種開放的方法,用于說明、可視化、構建和編寫一個正在開發(fā)的、面向對象的、軟件密集系統(tǒng)的 制品的開放方法。UML展現(xiàn)了一系列最佳工程實踐,這些最佳實踐在對大規(guī)模,復雜系統(tǒng)進行建模方面,特
16、別是在軟件架構層次已經(jīng)被驗證有效。 UML集成了Booch,OMT和面向對象軟件工程的概念,將這些方法融合為單一的,通用的,并且可以廣泛使用的建模語言。UML打算成為可以對并發(fā)和分布式系統(tǒng)的標準建模語言。 UML 并不是一個工業(yè)標準,但在Object Management Group的主持和資助下,UML正在逐漸成為工業(yè)標準。OMG 之前曾經(jīng)呼吁業(yè)界向其提供有關對象導向的理論及實現(xiàn)的方法,以便制作一個嚴謹?shù)能浖UZ言(Software Modeling Language)。 有很多業(yè)界的領袖亦真誠地回應OMG,幫助她建立一個業(yè)界標準。在UML系統(tǒng)開發(fā)中有三個主要的模型:* *功能模型*: 從
17、用戶的角度展示系統(tǒng)的功能,包括用例圖。* *對象模型*: 采用對象,屬性,操作,關聯(lián)等概念展示系統(tǒng)的結構和基礎,包括類圖。* *動態(tài)模型*: 展現(xiàn)系統(tǒng)的內部行為。包括序列圖,活動圖,狀態(tài)圖。在學習開源軟件時如何使用UML,有以下一些經(jīng)驗和忠告:* 不用使用工具,自動化的生成UML。自己手繪或者用Umbrello這樣的開源工具自己繪制,將大大提高閱讀并理解代碼的能力。* 首先建立對象的靜態(tài)模型,也就是先畫“類圖”。* 在UML規(guī)范之外,可以加一些輔助自己記憶的符號,這個沒有一定的規(guī)矩,方便好記就行。* 其次在動態(tài)理解的過程中,對關鍵的執(zhí)行路徑,畫出時序圖(Sequence Diagram),將有
18、助于深入理解項目的執(zhí)行過程。* 對于非面向對象的軟件項目,可以參照類圖與組件圖的模式,畫出模塊圖。也有助于加深理解。* UML本身有越來越復雜,越來越學術化的傾向,要適可而止。# 5.1.6. 外部文檔開源項目的文檔水平,有如下幾個層次:* 沒有文檔(比這個更糟糕的,是有一些錯漏的,長時間沒有更新的垃圾文檔)* 有一個根據(jù)代碼注釋自動生成的XXDoc,通常這樣的文檔,價值很低。還不如直接去看代碼。* 有一個簡單的綜述性質的文檔,至少告訴你一個項目的大概。* 有完整的項目文檔,這樣的項目已經(jīng)非常罕見了。* 有各國志愿者幫助翻譯的多語言文檔。這樣的項目,通常已經(jīng)是世界一流的項目了。* 有專門的文檔
19、、博客、甚至圖書,XX項目源碼解讀之類。一般只有Linux、MySQL這樣的項目,才有這樣的待遇吧。一般來說,外部文檔的可信度并不高,而且往往過時。不到走投無路,我不建議找外部文檔來幫助理解。當然,一些太復雜的項目,作為入門導引,看看也無妨。ZoomQuiet補充:可以借用 CMMI 的幾個層次來類比出文檔的層次:* 0級: 無文檔* 1級: 號稱有文檔* 2級: 有可用文檔* 3級: 文檔完備* 4級: 文檔豐富* 5級: 文檔開放,可持續(xù)完善# 5.2. 動態(tài)理解所謂動態(tài)理解,就是讓項目運行起來,在運行項目的過程中,理解一個項目是如何運行的。(這個有點像繞口令了。)# 5.2.1. 輸出日
20、志所謂日志,在軟件領域,通常是指程序運行的一種記錄。開發(fā)者與維護人員,可以通過分析日志,了解程序的運行狀況。日志輸出的數(shù)量多寡,可以分為以下幾種:* 完全沒有輸出(這不是一種好的做法)* 有出錯與崩潰時的輸出日志(主要用于排除故障)* 打開某個參數(shù)配置的開關,例如將日志級別修改為debug,將會輸出更多的日志信息(主要用于調試程序)* 為了理解特定的片段,直接修改代碼,增加更多的日志輸出,甚至將代碼執(zhí)行過程中的所有相關變量,全都輸出出來。(用于查找疑難雜癥,深入理解源代碼等目的。)ZoomQuiet補充:同前,應該使用相同的遞進分析層次* 0級: 無日志* 1級: 號稱有日志* 2級: 有可用
21、日志* 3級: 日志級別完備* 4級: 日志粒度可調節(jié)* 5級: 有通用日志服務,可集中分析一個比較完善的開源項目,通常會輸出一些日志,如果你搜索整個項目的源代碼,都找不到(log,logger,logging)這樣的關鍵字,那就比較糟糕了。通過修改源代碼,以增加更多的日志輸出,是我們常用的一種手段,最好是能夠找到項目中可供參考的輸出日志的辦法,照著那個例子來改寫。如果實在找 不到,或者調用存在一些陷阱,也可以自己純粹手工的添加日志輸出代碼。簡單的舉一個ruby的例子,下面的這一行代碼,就可以輸出一段內容到日志文件里去 了。 File.open(temp.log,a) |f| f.puts(l
22、og info) 輸出日志,能夠解決大多數(shù)情況下的理解需求,唯一可能會有陷阱的,則是*多線程程序輸出的日志*,因為每次輸出的內容可能次序不一致,因此需要特別小心。# 5.2.2. 設置斷點與單步跟蹤IDE有一個重要的好處,就是可以幫助程序員調試程序。在一個圖形化界面里,跟蹤調試程序,有著純文字界面難以比擬的便利性。!(images/debug-NetBeans-IDE-7.0.png)Netbeans IDE 7.0 調試PHP的程序片段在一個寬屏的顯示器里,同時顯示源代碼樹、對象結構、當前執(zhí)行到的代碼行、當前的各種變量值、調用序列、各個線程、輸出內容等等等等,還是很爽的。當然,要使得IDE能
23、夠調試一個開源項目,還是有很多瑣碎的事情需要處理。例如:* 如何在IDE中打開一個項目* 如何在IDE中配置一個項目的依賴項* 如何在IDE中編譯并運行一個項目* 如何在IDE中設置斷點這些如何,因語言、平臺、IDE、版本、具體項目的不同,而有所區(qū)別。這里沒法給出一個周到全面的解決方案,但是可以給一些搜索方面的建議:* 假設要在Netbeans 7中打開一個開源的Java項目,可以搜索“how to open java project in netbeans 7”,然后我們可以找到一些文檔: * /kb/docs/java/project-setup.html * /questions/438
24、2619/how-can-i-open-non-netbeans-java-project-using-netbeans當然,一定會有各種讓人撓頭的問題,各位多多嘗試吧。另外推薦一本書,是張銀奎寫的軟件調試,大部頭。但是的確是一本好書!# 5.2.3. 拋出異常有很多種語言,都支持異常處理,以及手動拋出異常。在特定的位置,將整個調用序列打印出來,可以方便我們快速的找到整個項目,是從何處開始,又是如何一層一層的調用,最終到達我們設置拋出異常的位置的。在Java語言中,我們可以這么寫:(new Exception().printStackTrace();在PHP語言中,我們可以直接調用函數(shù):deb
25、ug_backtrace();或者debug_print_backtrace();在Ruby語言中,我們可以這么寫: begin 1/0 rescue = exception puts exception.backtrace end其他種類的語言,建議各位可以自行Google。在調用了類似的函數(shù)之后,我們可以或者類似如下這樣的輸出: java.lang.Exception at org.jruby.lexer.yacc.LexerSource.getSource(LexerSource.java:147) at org.jruby.parser.Parser.parse(Parser.java
26、:122) at org.jruby.Ruby.parseFile(Ruby.java:1965) at org.jruby.Ruby.parseFile(Ruby.java:1969) at org.jruby.Ruby.parseFromMain(Ruby.java:364) at org.jruby.Ruby.runFromMain(Ruby.java:327) at org.jruby.Main.run(Main.java:214) at org.jruby.Main.run(Main.java:100) at org.jruby.Main.main(Main.java:84) 以上信
27、息,清楚的現(xiàn)實了某某源文件的某某行,發(fā)生了一個調用,進而在下一個源文件中的某個函數(shù),被調用了。于是,我們就可以從拋出異常的代碼,開始逐級回溯,閱讀相應的代碼片段。# 5.2.4. 修改代碼,破壞性嘗試在初步理解了項目代碼之后,可以嘗試做一些簡單的修改,看看會發(fā)生什么。* 編譯錯誤靜態(tài)類型的語言,在這方面有更多優(yōu)勢,通過編譯時給出的錯誤提示,你可以知道自己改壞了什么,以及為什么它被改壞了。比較令人頭痛的,是用了各種復雜的模板語法的C+語言,這時候編譯器可能會給出一些令人莫名其妙的報錯信息,那你就抓瞎了。不過,開源初學者,一上手就去啃復雜的C+代碼,也很難說是明智的選擇。* 運行時報錯與編譯期報錯
28、類似,你也可以通過閱讀一堆一堆的報錯信息,了解代碼是如何運作的,以及為什么原來不報錯,現(xiàn)在就開始報錯了。不過,觸發(fā)運行時報錯,可能會有些困難,這涉及到你修改的代碼,是處于主線還是支線位置,以及有多少相關功能,依賴于你所修改的代碼。* 功能失效可以嘗試注釋掉一些代碼,看看會發(fā)生什么事情,比如某個功能按鈕失靈甚至消失。當然,對于JavaScript代碼,我們常常會喜歡在某些位置加上alert,看看什么時候會彈出一個消息提示框,缺什么事都沒有完成。* 走走捷徑我們常常會看到的一類代碼,是長期發(fā)展完善后的產(chǎn)物。從接收消息,要處理某某事件開始,到真正去完成某某功能,其間還做了很多的額外工作,比如數(shù)據(jù)校驗
29、,紀錄日志,觸發(fā)其他消息等等,將這些代碼全部注釋掉,大多數(shù)功能都還是正常的,但是,會出各種意外,可以進一步做出各種嘗試。(詳見5.3. 主線與支線)# 5.2.5. 工具有很多輔助代碼閱讀工具,簡單的介紹一些* IDE類既然是IDE,自然大多數(shù)都已經(jīng)集成了相當不錯的代碼閱讀功能,索引,反查,標注,自由跳轉,通常都應有盡有。比較好的IDE,有Eclipse、Netbeans、IntelliJ IDEA、Visual Studio(Express)等等,各種面向專門語言的IDE還有很多,可以自行在Google搜索“Best XXX IDE”。進階閱讀:18 個最佳代碼編輯器/IDE推薦(/news
30、/24235)* 傳統(tǒng)神器超牛插件所謂傳統(tǒng)神器,自然是指Emacs與VIM,這兩款神器不能簡單的以是否IDE來劃分。他們都帶有眾多的插件,可以擴展出驚人的靈活性。推薦閱讀兩篇文章:Emacs和它的朋友們閱讀源代碼篇(/reading-source-code-cn)* 專業(yè)工具專業(yè)工具,通常也是由商業(yè)公司開發(fā)的收費軟件,人家要掙錢,自然也要有拿得出手的好東西。這里推薦兩款軟件,更多介紹,請自行搜索相關介紹與教程:* Understand(/index.php)* source insight(/)* 軟件工程相關工具所謂軟件工程的相關工具,主要是一些與面向對象概念相關的UML工具,比如IBM的R
31、ational Rose、Sybase的Power Desinger、還有Borland的Together,都是大公司出品的一些大家伙??梢詫⒚嫦驅ο笳Z言(主要是Java)的項目,轉換輸出成為UML的各種圖,有一定的參考價值,玩玩也不錯。# 5.3. 主線與支線一個開源項目,我們總可以從中找到主要的功能與次要的、輔助的功能。一個系統(tǒng)從啟動開始,到完成最核心的功能、任務,最后成功結束,就是一條主線。而各種可能達到的次要功能,就是支線。一個項目在最初創(chuàng)建的時候,總有一組想要實現(xiàn)的核心功能,其他都是次要的目標。比如:我做一個BBS,要讓用戶能夠登錄,發(fā)言(或者回復評論他人的發(fā)言),退出登錄。這就是主
32、線。而:設置用戶頭像,版主的各種權限與管理功能,頂、 踩功能,帖子內投票,站內短信等等等等,都可以算是次要功能。在一個項目中,首先清理出主線以及其相關的代碼,是一個非常必要的工作,主要的入手處,是項目的Readme文檔。另外,對于某一領域的基礎知識,也非常必要。# 5.3.1. 尋找入口看一堆代碼,總要找一個入手處。而不同的項目,入手處是不一樣的。舉幾個例子:* 一個滿足MVC模式的Web項目,通常會有多個Controller,這些Controller就是代碼的入口處。如何才能找到某個訪問路徑對應的Controller呢?這就需要去看Route的定義了。* 一個較為普通的C/C+/Java項目
33、,通常會有一個或者好幾個含有main()函數(shù)的文件,那個就是代碼的入口處。* Make、NMake、Rake、Ant、Maven等等,都是各種不同語言與項目,可能會用到的批處理任務管理工具,有很多的項目,是以這些配置文件中描述的文件,作為入口的,這自然需要理解相應的配置文件的語法規(guī)則。* 5.2.3節(jié)中,描述的拋出異常的方法,也是找到入口的辦法。# 5.3.2. 跟蹤關鍵流程所謂跟蹤關鍵流程,核心要義,就是學會使用調試器。包括集成開發(fā)環(huán)境(IDE)與命令行方式的調試器(例如GDB)。 無論何種調試工具,其基本的用法都是一致的:* 設置斷點* 運行時中斷* 單步跟蹤* 查看或修改內存中的各種變量
34、在下圖所示的Java代碼debug截圖中:* 我們將斷點設置在CookieExample.java的第43行(紅色行)。* 以debug方式運行后,代碼將會停留在第43行。* 我們連續(xù)按六次F8,表示代碼向下執(zhí)行六行,目前停留在第51行(綠色行)。* 在窗口的下方,我們可以看到當前內存中的各種變量,例如title這個變量,就是String類型,值等于:Cookies Example* 在屏幕的左下角,我們可以看到這個java源文件包含兩個方法與一個實例變量。* 在屏幕的左上角,我們可以看到這個項目執(zhí)行的調用序列,撇開那些外部框架的代碼,我們可以認為是ExampleFilter.java的doF
35、ilter方法在第101行時調用了CookieExample.java中的doGet方法。!(images/debug.png)最笨的辦法,就是一行一行的跟著代碼的執(zhí)行,反復的觀察并理解,這些代碼究竟做了哪些事情。隨著對代碼理解的加深,我們可以每次多執(zhí)行幾行,跳過一些已經(jīng)沒有疑問的代碼,在更多的地方設置斷點,甚至可以在運行時,試著修改某些變量的值,看看會發(fā)生什么變化。跟蹤關鍵流程,最容易犯的錯誤,就是單步跟蹤時,迷失在代碼的叢林里,暈頭轉向。及時識別出不太重要的代碼(比如,試著注釋掉一些代碼段,看看會不會造成太大的影響),并且跳過這些段落,是保證有效追蹤的關鍵。說實話,要將代碼調試這個事情講解
36、清楚,值得寫整整一本書,有興趣深入學習的同學,可以找一些專門的書籍來看,例如:軟件調試的藝術(/subject/4111413/)、Debug Hacks中文版(/subject/6799412/)# 5.3.3. 理解插件體系在較為現(xiàn)代的開源項目之中,常常會出現(xiàn)某種插件體系結構,整個系統(tǒng)可以分為三個主要的部分:核心功能+插件管理框架+擴展插件。通過這樣的體系結構,開源項目的主創(chuàng)人員,可以集中精力實現(xiàn)自己設想中的功能,而將其他擴展功能的開放任務,交給有興趣的外部開發(fā)者,一方面可以吸引更多的人來參與,另一方面,也可以有效的隔離外部開發(fā)者對于核心架構的干擾。因此,現(xiàn)在很多參與開源項目的方式,并非一
37、開始就可以加入核心代碼的開發(fā),而是圍繞著插件體系,為系統(tǒng)新增一個插件,或者修改其中的一個插件。對于復雜的系統(tǒng)而言,有時候一個插件,也成為了一個獨立的開源項目,可以自由的參與其中。不同的語言,不同的開源項目,可能具有不同的插件體系,較為著名的,有Eclipse的插件體系OSGi、FireFox的插件體系、jQuery的插件體系、Redmine的插件體系以及Joomla!的插件體系等等。在維基百科上有一個關于插件的概要介紹:/wiki/Plug-in_(computing)(/wiki/Plug-in_(computing)要理解某個項目的插件體系,通常需要找到專門的介紹文檔,仔細閱讀。例如Jav
38、a的OSGi,已經(jīng)非常的復雜,值得專門寫N本書來深入介紹:豆瓣搜索OSGi的結果(/search?cat=1001&q=OSGi)這里做一些簡單的介紹的,是在不看文檔的情況下,試著從代碼尋找答案的初步方法。以Joomla!CMS為例:將Joomla的源代碼解壓縮以后,我們發(fā)現(xiàn)有三個目錄的名字,較為可疑:components、modules、plugins,看上去都是作為擴展插件而存在的。首先查看components目錄,里面有一堆的以com_開頭的文件夾,看起來就是每一個文件夾,是一個組件。進入其中的一個文件夾:com_banners。打開banners.php,發(fā)現(xiàn)有三行關鍵代碼: $con
39、troller = JControllerLegacy:getInstance(Banners); $controller-execute(JFactory:getApplication()-input-get(task); $controller-redirect();其中Legacy這個單詞,引起了我的注意:看來component是一種遺留類型的插件,每個插件,也以MVC的風格寫成。在源文件里搜索“class JControllerLegacy”,于是發(fā)現(xiàn)了一組文件: /libraries/legacy/controller/admin.php /libraries/legacy/cont
40、roller/form.php /libraries/legacy/controller/legacy.php看來都是用來處理遺留的擴展系統(tǒng)的controller的。繼續(xù)閱讀com_banners里的代碼,在models目錄下,有一個banner.php文件,在其中我們又看到了一行值得注意的代碼:class BannersModelBanner extends JModelLegacy看來是與處理遺留擴展系統(tǒng)中的controller類似,用來處理model的。在源文件里搜索“class JModelLegacy”,于是發(fā)現(xiàn)了另一組文件: /libraries/legacy/model/admi
41、n.php /libraries/legacy/model/form.php /libraries/legacy/model/item.php /libraries/legacy/model/legacy.php /libraries/legacy/model/list.php從這8個文件入手,我想就可以看明白component的擴展機制了。在看modules目錄下的文件,也是一堆以mod_開頭的文件夾,打開其中一個mod_banners/mod_banners.php也能發(fā)現(xiàn)一行值得注意的代碼: require JModuleHelper:getLayoutPath(mod_banners,
42、 $params-get(layout, default);在源文件中搜索“class JModuleHelper”,就會另外發(fā)現(xiàn)一個文件:/libraries/legacy/module/helper.php看來是處理遺留擴展系統(tǒng)中的modules的,讀懂這個文件,應該就可以理解Module的擴展機制了。最后看看plugins下的代碼,與component和module不同,plugins是可以分層的,在authentication目錄下,還有gmail、joomla、ldap三個目錄,每個目錄下,又有兩個關鍵的文件,而且都與目錄名(插件名)相同,gmail.php、gmail.xml??磥?/p>
43、一個是gmail認證的實現(xiàn)文件,一個是這個插件的配置文件。在源文件中搜索“class JPlugin”,可以發(fā)現(xiàn)兩個文件: /libraries/joomla/plugin/helper.php /libraries/joomla/plugin/plugin.php配合這兩個文件,加上插件目錄下的.php與.xml文件,應該就可以較為清楚的理解plugins的實現(xiàn)機制了。# 5.4. 外圍代碼# 5.4.1. 必須存在的外圍功能當我們閱讀一個開源項目的代碼時,當時首先是找到主線代碼,然后努力去理解其核心的內容。但是,一個完整的開源項目,必須存在很多外圍的功能與代碼,除了文檔之外,還有其他一些必
44、不可少的內容。* 例如,一個C/C+的開源項目,通常會有的Makefile文件,已經(jīng)較為正規(guī)一點的項目都會提供的configure腳本文件。沒有這樣的文件,想要自己編譯整個項目,幾乎是不可能的。* 再比如:一個項目如果與數(shù)據(jù)庫相關,必備的數(shù)據(jù)庫建表、初始化數(shù)據(jù)燈工作,就得有一些工具來輔助。最糟糕的,是寫一個txt文檔,讓你照著做,好一點的是提供一個或一組SQL,供人執(zhí)行,這方面Rails做得非常的到位,我們通??梢栽谝粋€Rails的開源項目下,找到一個/db/migrate的目錄,這里面提供的腳本,是專門用于數(shù)據(jù)遷移的。* 一些好的開源項目,會允許多種不同的運行模式,例如在運行時加上debug
45、參數(shù),就能夠輸出更多的調試信息,供使用者查找可能存在的問題。在閱讀理解代碼的時候,這些調試代碼也是輔助理解開源項目的好方法。* 依賴管理也是一個麻煩的事情,這方面有兩個較為好的榜樣,Ruby的開源項目往往會在根目錄下附帶一個Gemfile文件,下載源碼之后,只要再執(zhí)行一個bundle install即可。Node.JS也有類似的好習慣,根目錄下的package.json文件,就是類似的定義包依賴的文件,只要執(zhí)行npm install,就萬事大吉。* 負責任的開源項目,在提供源代碼之前,就已經(jīng)做了充分的測試,而且通常是按照單元測試的規(guī)范來做的。對于C/C+項目,我們在make之后,一般可以mak
46、e test,或者對于Java項目,我們可以ant test。對于ruby項目,我們可以rake test或者rspec xxx.rb* 當然,負責任的開源框架,通常還會提供demo與example,連帶單元測試的介紹,我們都會再下面的兩節(jié),詳細展開。# 5.4.2. 單元測試在軟件開發(fā)的各種最佳實踐中,我認為最具有其他性的,是“測試驅動開發(fā)(TDD)”,而最容易誤導開發(fā)者的,則是“設計模式(DP)”,在閱讀開源項目的代碼時,如果我看到編寫完整而全面的單元測試代碼,我自然會對整個項目的質量高看一眼。而如果我在源代碼里看到了充斥著陳腐味道的各種設計模式的名字時,我會猜測這是一個“玩具項目”也就是
47、那種經(jīng)驗不足的開發(fā),學習練手的項目。單元測試,也有高下之分,好的單元測試,會測試關鍵邏輯與核心算法;而敷衍了事的單元測試,則會去測試get/set這樣的代碼,看起來一堆assert,卻毫無價值。閱讀單元測試的代碼,也有一種弊端,就是因為陷入點點滴滴的瑣碎細節(jié),進而迷失了方向。所以,不要在一開始就去閱讀單元測試的代碼,而是先對項目的整體有所了解之后,再開始閱讀。再者,好的單元測試的代碼,會特別注重使用場景的構建:某個功能點,在什么情況下,被預期會做出什么樣的反應。而構建使用場景的代碼,主要集中在startUp這樣的函數(shù)里,以及各種Mock對象的構造之中,因此,關注startUp與Mock創(chuàng)建,而
48、不是僅僅去看一個一個的testXXX()方法,會更容易理解開發(fā)者的意圖。# 5.4.3. demo/example我們可以簡單的將開源項目劃分成兩類,一類是給最終用戶使用的項目;一類是基于這個項目,可以繼續(xù)做開發(fā)的。對于第二類項目,有可以分為幾類:開發(fā)框架(各種Web MVC框架)、基礎服務(MySQL、Message Queue)、可以被插件擴展的軟件(FIrefox、Chrome)、編程語言(Ruby、Python、NodeJS)、模板引擎(SaSS、Less、HAML)等等。所有這第二類項目,都需要告訴使用者,應該如何使用自己的項目,給出開發(fā)文檔、入門教程、指南、手冊之類,當然多多益善。
49、但是,最能夠指導用戶學習的,卻是demo或者example。一個負責任的開源項目,就應該給出足夠覆蓋自身功能特性的例子,以指導開發(fā)者的使用,同時也是對自身功能特性的一種全面的檢驗。對于初學者而言,運行demo,嘗試修改,嫁接組合,其實就是類似理解一個開源項目的,較小規(guī)模的閱讀與理解。所以,好的demo,不僅應該是豐富,最好還是循序漸進的。從Hello World級的demo,到PetShop級的demo,都會對開發(fā)者有很大的幫助。至于如何閱讀理解這些demo,其實就是將之看成一個小型的開源項目,然后應用本章的各種方法而已,在此不再贅述。# 5.5. 知其所以然有一句俗語叫做:“知其然,更要知其所以然”。用在任何學習科目上,幾乎都是恰當?shù)摹1菊陆凶隼斫忾_源項目,而之前的4個小節(jié),可以說都是屬于“知其然”的功夫。如何才能知其所以然呢?# 所以然包括哪些內容?往大了說,整個這份文檔,希望幫助讀者達到的,就是能夠對于開源軟件“知其所以然”。這樣才算是真正提高了軟件開發(fā)的能力。因此,我們可以將“架構決策”、“代碼風格”、“領域
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
- 4. 未經(jīng)權益所有人同意不得將文件中的內容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內容本身不做任何修改或編輯,并不能對任何下載內容負責。
- 6. 下載文件中如有侵權或不適當內容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 石河子大學《藥物分析實驗》2022-2023學年第一學期期末試卷
- 石河子大學《現(xiàn)代教育技術》2023-2024學年第一學期期末試卷
- 石河子大學《紀錄片賞析》2023-2024學年第一學期期末試卷
- 沈陽理工大學《自動檢測技術》2022-2023學年第一學期期末試卷
- 沈陽理工大學《色彩構成》2021-2022學年第一學期期末試卷
- 沈陽理工大學《建筑力學》2021-2022學年第一學期期末試卷
- 沈陽理工大學《后期特效》2023-2024學年第一學期期末試卷
- 沈陽理工大學《電工與電子技術實驗》2021-2022學年期末試卷
- 沈陽理工大學《測量學》2021-2022學年第一學期期末試卷
- 海商法修改船舶融資租賃合同
- 《市場營銷》教案(第3周)市場營銷環(huán)境分析
- 租地種香蕉合同
- 上海市虹口區(qū)2024學年第一學期期中考試初三物理試卷-學生版
- 舊市場提升改造方案
- 湖北漢江王甫洲水力發(fā)電限責任公司公開招聘工作人員【6人】高頻難、易錯點500題模擬試題附帶答案詳解
- 統(tǒng)編版 七年級上冊(2024修訂) 第四單元 13 紀念白求恩 課件
- 外匯兌換居間勞務協(xié)議
- 國開(甘肅)2024年春《地域文化(專)》形考任務1-4終考答案
- 檔案整理及數(shù)字化服務方案(技術標 )
- 《宏觀經(jīng)濟學乘數(shù)論》PPT課件.ppt
- 警務監(jiān)督員表態(tài)發(fā)言(共4篇)
評論
0/150
提交評論