日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

如何看懂源代码--(分析源代码方法)(转)

發(fā)布時(shí)間:2025/5/22 编程问答 19 豆豆
生活随笔 收集整理的這篇文章主要介紹了 如何看懂源代码--(分析源代码方法)(转) 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

ps:近日,也在看別人寫(xiě)的框架,有點(diǎn)水霧繚繞的感覺(jué),找到這篇好文,并分享之

摘自(繁體中文Traditional Chinese):http://www.ithome.com.tw/itadm/article.php?c=47717

下文為經(jīng)過(guò)Google翻譯過(guò)的簡(jiǎn)體中文版:


我們?cè)趯?xiě)程式時(shí),有不少時(shí)間都是在看別人的代碼。?
例如看小組的代碼,看小組整合的守則,若一開(kāi)始沒(méi)規(guī)劃怎么看,?
就會(huì)“嚕看嚕苦(臺(tái)語(yǔ)) ”?

不管是參考也好,從開(kāi)源抓下來(lái)研究也好,為了了解箇中含意,在有限的時(shí)間下,不免會(huì)對(duì)龐大的源代碼解讀感到壓力。?
網(wǎng)路上有一篇關(guān)于分析看代碼的方法,做為程式設(shè)計(jì)師的您,不妨參考看看,?
換個(gè)角度來(lái)分析。?也能更有效率的解讀你想要的程式碼片段。?

六個(gè)章節(jié):?
( 1 )讀懂程式碼,使心法皆為我所用。?
( 2 )摸清架構(gòu),便可輕松掌握全貌。?
( 3 )優(yōu)質(zhì)工具在手,讀懂程式非難事。?
( 4 )望文生義,進(jìn)而推敲組件的作用。?
( 5 )找到程式入口,再由上而下抽絲剝繭。?
( 6 )閱讀的樂(lè)趣,透過(guò)程式碼認(rèn)識(shí)作者。?


閱讀他人的程式碼( 1 ) ---讀懂程式碼,使心法皆為我所用?

程式碼是別人寫(xiě)的,只有原作者才真的了解程式碼的用途及涵義。許多程式人心里都有一種不自覺(jué)的恐懼感,深怕被迫去碰觸其他人所寫(xiě)的程式碼。但是,與其抗拒接收別人的程式碼,不如徹底了解相關(guān)的語(yǔ)言和慣例,當(dāng)成是培養(yǎng)自我實(shí)力的基石。

對(duì)大多數(shù)的程式人來(lái)說(shuō),撰寫(xiě)程式碼或許是令人開(kāi)心的一件事情,但我相信,有更多人視閱讀他人所寫(xiě)成的程式碼為畏途。許多人寧可自己重新寫(xiě)過(guò)一遍程式碼,也不愿意接收別人的程式碼,進(jìn)而修正錯(cuò)誤,維護(hù)它們,甚至加強(qiáng)功能。?

這其中的關(guān)鍵究竟在何處呢?若是一語(yǔ)道破,其實(shí)也很簡(jiǎn)單,程式碼是別人寫(xiě)的,只有原作者才真的了解程式碼的用途及涵義。許多程式人心里都有一種不自覺(jué)的恐懼感,深怕被迫去碰觸其他人所寫(xiě)的程式碼。這是來(lái)自于人類(lèi)內(nèi)心深處對(duì)于陌生事物的原始恐懼。?

讀懂別人寫(xiě)的程式碼,讓你收獲滿(mǎn)滿(mǎn)?
不過(guò),基于許多現(xiàn)實(shí)的原因,程式人時(shí)常受迫要去接收別人的程式碼。例如,同事離職了,必須接手他遺留下來(lái)的工作,也有可能你是剛進(jìn)部門(mén)的菜鳥(niǎo),而同事經(jīng)驗(yàn)值夠了,升級(jí)了,風(fēng)水輪流轉(zhuǎn),一代菜鳥(niǎo)換菜鳥(niǎo)。甚至,你的公司所承接的專(zhuān)案,必須接手或是整合客戶(hù)前一個(gè)廠商所遺留下來(lái)的系統(tǒng),你們手上只有那套系統(tǒng)的原始碼(運(yùn)氣好時(shí),還有數(shù)量不等的文件) 。?

諸如此類(lèi)的故事,其實(shí)時(shí)常在程式人身邊或身上持續(xù)上演著。許多程式人都將接手他人的程式碼,當(dāng)做一件悲慘的事情。每個(gè)人都不想接手別人所撰寫(xiě)的程式碼,因?yàn)椴幌牖〞r(shí)間去探索,寧可將生產(chǎn)力花在產(chǎn)生新的程式碼,而不是耗費(fèi)在了解這些程式碼上。?

很遺憾的是,上述的情況對(duì)程式人來(lái)說(shuō)很難避免。我們總是必須碰觸到其他人所寫(xiě)成的程式碼,甚至必須了解它,加以修改。對(duì)于這項(xiàng)需求,在現(xiàn)今開(kāi)放原始碼的風(fēng)氣如此盛行的今日,正如之前的“程式設(shè)計(jì)2.0 ”文中所提到的,你可以透過(guò)開(kāi)放原始碼學(xué)習(xí)到新的技術(shù),學(xué)習(xí)到高手的架構(gòu)設(shè)計(jì),大幅提高學(xué)習(xí)的效率及效果。你甚至可以直接自開(kāi)放原始碼專(zhuān)案中抽取,提煉出自己所需的程式碼,站在巨人的肩膀上,直接由彼端獲得所需的生產(chǎn)力。從這個(gè)觀點(diǎn)來(lái)看,讀懂別人所寫(xiě)的程式碼,就不再只是從負(fù)面觀點(diǎn)的“被迫接收” ,而是極具正面價(jià)值的“汲取養(yǎng)份。 ”?

先了解系統(tǒng)架構(gòu)與行為模式,再細(xì)讀?
倘若撰寫(xiě)程式碼是程式人的重要技藝之一,那么讀懂別人的程式碼,接著加以修改,也勢(shì)必是另一個(gè)重要的技藝。?

如果你不能熟悉這項(xiàng)工作,不僅在遭逢你所不愿面對(duì)的局面時(shí),無(wú)法解決眼前接手他人程式碼的難題,更重要的是,當(dāng)你看著眼前現(xiàn)成的程式碼,卻不知如何從中擷取自己所需,導(dǎo)致最后只能入寶山空手回,望之興嘆。?

接觸他人的程式碼,大致上可以分為三種程度:一,了解,二,修改,擴(kuò)充,三,抽取,提煉。了解別人的程式碼是最基礎(chǔ)的工作,倘若不能了解自己要處理的程式碼,就甭論修改或擴(kuò)充,更不可能去蕪存菁,從中萃取出自己所需,回收再利用別人所撰寫(xiě)的程式碼。雖說(shuō)是“閱讀” ,但程式碼并不像文章或小說(shuō)一樣,透過(guò)這種做法,便能夠獲得一定程度的了解。閱讀文章或小說(shuō)時(shí),幾乎都是循序地閱讀,你只消翻開(kāi)第一頁(yè),一行行閱讀下去即可。但是,有許多程式人在試著閱讀其他人的程式碼時(shí),卻往往有不知如何讀起的困難。?

或許找到系統(tǒng)的第一頁(yè)(也就是程式碼執(zhí)行的啟始點(diǎn))并不難,但是復(fù)雜度高的系統(tǒng),有時(shí)十分龐大,有時(shí)千頭萬(wàn)緒。?

從程式碼的啟始點(diǎn)開(kāi)始讀起,一來(lái)要循序讀完所有的程式碼曠日費(fèi)時(shí),二來(lái)透過(guò)這種方式來(lái)了解系統(tǒng),很難在腦中構(gòu)建出系統(tǒng)的面貌,進(jìn)而了解到系統(tǒng)真正的行為。所以,閱讀程式碼的重點(diǎn),不在于讀完每一行程式碼,而是在于有效率地透過(guò)探索及閱讀,從而了解系統(tǒng)的架構(gòu)及行為模式。以便在你需要了解任何片段的細(xì)節(jié)實(shí)作時(shí),能夠很快在腦上對(duì)映到具體的程式碼位置,直到那一刻,才是細(xì)讀的時(shí)機(jī)。?

熟悉溝通語(yǔ)言與慣例用語(yǔ)?
不論如何,有些基本的準(zhǔn)備,是閱讀他人程式碼時(shí)必須要有的。?

首先,你最好得了解程式碼寫(xiě)成的程式語(yǔ)言。想要讀懂法文寫(xiě)成的小說(shuō),總不能連法文都不懂吧。有些情況則很特殊。我們雖然不懂該程式碼撰寫(xiě)所用的語(yǔ)言,但是因?yàn)楝F(xiàn)代語(yǔ)言的高階化,而且流行的程式語(yǔ)言多半都是血統(tǒng)相近,所以即使不那么熟悉,有時(shí)也可勉力為之。?

除了認(rèn)識(shí)所用語(yǔ)言之外,再來(lái)就是要先確認(rèn)程式碼所用的命名慣例(命名慣例) 。了解命名慣例很重要,不同的程式人或開(kāi)發(fā)團(tuán)隊(duì),差異可能很大。?
這命名慣例涵蓋的范圍通常包括了變數(shù)的名稱(chēng),函式的名稱(chēng),類(lèi)別(如果是物件導(dǎo)向的話)的名稱(chēng),原始碼檔案,甚至是專(zhuān)案建構(gòu)目錄的名稱(chēng)。倘若使用了像設(shè)計(jì)模式之類(lèi)的方法,這些名稱(chēng)更有一些具體的表述方式。?

命名慣例有點(diǎn)像是程式人在程式語(yǔ)言之上,另行建構(gòu)的一組溝通行話。程式人會(huì)透過(guò)共通約束,遵守的命名慣例,來(lái)表達(dá)一些較高階的概念。例如,有名的匈牙利式命名法,便將變數(shù)名稱(chēng)以屬性,型別,說(shuō)明合并在一起描述。對(duì)程式人來(lái)說(shuō),這種方式能夠提供更豐富的資訊,以了解該變數(shù)的作用及性質(zhì)。?

對(duì)程式碼閱讀來(lái)說(shuō),熟悉這個(gè)做法之所以重要,是因?yàn)楫?dāng)你了解整個(gè)系統(tǒng)所采用的慣例時(shí),你便能試著以他們所共同操用的語(yǔ)匯來(lái)進(jìn)行理解。倘若,不能了解其所用的慣例,那么這些額外提供的資訊,就無(wú)法為你所用。像以設(shè)計(jì)模式寫(xiě)成的程式碼,同樣處處充滿(mǎn)著模式的名稱(chēng),諸如:工廠,門(mén)面,代理等等。以這些名稱(chēng)指涉的類(lèi)別,也直接透過(guò)名稱(chēng),表達(dá)了它們自身的作用。對(duì)于懂得這命名慣例的讀者來(lái)說(shuō),不需要深入探索,也能很快捕捉到這些類(lèi)別的意義。?

當(dāng)你拿到一套必須閱讀的程式碼時(shí),最好先取得命名慣例的說(shuō)明文件。然而,并不是每套程式碼都附有此類(lèi)的說(shuō)明文件。另一個(gè)方式,就是自己到程式碼中,大略瀏覽一遍,有經(jīng)驗(yàn)的程式人可以輕易發(fā)掘出該系統(tǒng)所用的命名慣例。?

常見(jiàn)的命名方式不脫那幾類(lèi),這時(shí)候經(jīng)驗(yàn)就很重要,倘若你知道的慣例越多,就越能輕易識(shí)別他人所用的慣例。如果運(yùn)氣很糟,程式碼所用的慣例是前所未見(jiàn)的,那么你也得花點(diǎn)時(shí)間歸納,憑自己的力量找出這程式碼命名上的規(guī)則。?

掌握程式碼撰寫(xiě)者的心態(tài)與習(xí)慣?
大多數(shù)的程式碼,基本上都依循一致的命名慣例。不過(guò)運(yùn)氣更差的時(shí)候,一套系統(tǒng)中可能會(huì)充斥著多套命名慣例。這有可能是因?yàn)殚_(kāi)發(fā)團(tuán)隊(duì)由多組人馬所構(gòu)成,每組人馬都有不同的文化,而在專(zhuān)案開(kāi)發(fā)管理又沒(méi)有管控得宜所造成。最糟的情況,程式碼完全沒(méi)有明顯的慣例可言,這時(shí)候閱讀的難度就更高了。?

想要閱讀程式碼,得先試著體會(huì)程式碼作者的“心” 。想要這么做,就得多了解對(duì)方所使用的語(yǔ)言,以及慣常運(yùn)用的語(yǔ)匯。在下一回中,我們將繼續(xù)探討閱讀程式碼的相關(guān)議題。?



閱讀他人的程式碼( 2 ) -摸清架構(gòu),便可輕松掌握全貌

在本文中,我們的重點(diǎn)放在:要了解一個(gè)系統(tǒng),最好是采取由上至下的方式。先試著捕捉系統(tǒng)架構(gòu)性的觀念,不要過(guò)早鉆進(jìn)細(xì)節(jié),因?yàn)槟峭ǔ?duì)于你了解全貌,沒(méi)有多大的幫助。閱讀程式碼不需要從第一行讀起,我們的目的并不是在于讀遍每一段程式碼。

基于許多原因,程式人需要閱讀其他人所寫(xiě)成的程式碼。而對(duì)程式設(shè)計(jì)2.0時(shí)代的程式人來(lái)說(shuō),最正面的價(jià)值在于,能讀懂別人程式的人,才有能力從中萃取自己所需的程式,借以提高生產(chǎn)力。?

閱讀程式碼的目的,在于了解全貌而非細(xì)節(jié)?
想要讀懂別人程式碼的根本基礎(chǔ),便是了解對(duì)方所用的程式語(yǔ)言及命名慣例。有了這個(gè)基礎(chǔ)之后,才算是具備了基本的閱讀能力。正如我之前提到的─ ─想要讀懂法文寫(xiě)成的小說(shuō),總不能連法文都不懂吧。閱讀程式碼和閱讀文學(xué)作品,都需要了解撰寫(xiě)所用的語(yǔ)言及作者習(xí)用的語(yǔ)匯。?

但我們?cè)陂喿x文學(xué)作品通常是采循序的方式,也就是從第一頁(yè)開(kāi)始,一行一行地讀下去,依循作者為你鋪陳的步調(diào),逐漸進(jìn)到他為你準(zhǔn)備好的世界里。閱讀程式碼卻大大不同。我們很少?gòu)牡谝恍虚_(kāi)始讀起,因?yàn)槌撬呛芎?jiǎn)單的單執(zhí)行緒程式,否則很少這么做。因?yàn)橐沁@么做,就很難了解整個(gè)系統(tǒng)的全貌。是的,我們這邊提到了一個(gè)重點(diǎn),閱讀程式碼的目的在于了解系統(tǒng)的全貌,而不是在于只是為了地毯式的讀遍每一段程式碼。?

就拿物件導(dǎo)向程式語(yǔ)言所寫(xiě)成的系統(tǒng)來(lái)說(shuō),整個(gè)系統(tǒng)被拆解,分析成為一個(gè)個(gè)獨(dú)立的類(lèi)別。閱讀個(gè)別類(lèi)別的程式碼,或許可以明白每項(xiàng)類(lèi)別物件個(gè)別的行為。但對(duì)于各類(lèi)別物件之間如何交互影響,如何協(xié)同工作,又很容易陷入盲人摸象的困境。這是因?yàn)楦黝?lèi)別的程式碼,只描述個(gè)別物件的行為,而片段的閱讀就只能造就片面的認(rèn)識(shí)。?

由上而下厘清架構(gòu)后,便可輕易理解組成關(guān)系?
如果你想要跳脫困境,不想浪費(fèi)大量時(shí)間閱讀程式碼,卻始終只能捕捉到對(duì)系統(tǒng)片段認(rèn)識(shí),就必須轉(zhuǎn)換到另一種觀點(diǎn)來(lái)看待系統(tǒng)。從個(gè)別的類(lèi)別行為著手,是由下至上(自下而上)的方法;在閱讀程式碼時(shí),卻應(yīng)該先采由上至下(自上而下)的方式。對(duì)程式碼的閱讀來(lái)說(shuō),由上至下意謂著,你得先了解整個(gè)系統(tǒng)架構(gòu)。?

系統(tǒng)的架構(gòu)是整個(gè)系統(tǒng)的骨干,支柱。它表現(xiàn)出系統(tǒng)最突出的特征。知道系統(tǒng)架構(gòu)究竟屬于那一種類(lèi)型,通常大大有益于了解系統(tǒng)的個(gè)別組成之間的靜態(tài)及動(dòng)態(tài)關(guān)系。有些系統(tǒng)因?yàn)樗玫募夹g(shù)或框架的關(guān)系,決定了最上層的架構(gòu)。例如,采用的Java Servlet的/ JSP的技術(shù)的應(yīng)用系統(tǒng),最外層的架構(gòu)便是以J2EE的(或起碼的J2EE中的Web容器)為根本。?

使用的Java Servlet的/ JSP的技術(shù)時(shí),決定了某些組成之間的關(guān)系。例如, Web容器依據(jù)web.xml中的內(nèi)容載入所有的Servlets ,聽(tīng)眾,以及過(guò)濾器。每當(dāng)語(yǔ)境發(fā)生事件(例如初始化)時(shí),它便會(huì)通知監(jiān)聽(tīng)類(lèi)別。每當(dāng)它收到來(lái)自客戶(hù)端的請(qǐng)求時(shí),便會(huì)依循設(shè)定的所有過(guò)濾器鏈,讓每個(gè)過(guò)濾器都有機(jī)會(huì)檢查并處理此一請(qǐng)求,最后再將請(qǐng)求導(dǎo)至用來(lái)處理該請(qǐng)求的Servlet的。?

當(dāng)我們明白某個(gè)系統(tǒng)采用這樣的架構(gòu)時(shí),便可以很容易地知道各個(gè)組成之間的關(guān)系。即使我們還不知道究竟有多少Servlets ,但我們會(huì)知道,每當(dāng)收到一個(gè)請(qǐng)求時(shí),總是會(huì)有個(gè)相對(duì)應(yīng)的服務(wù)器來(lái)處理它。當(dāng)想要關(guān)注某個(gè)請(qǐng)求如何處理時(shí),我應(yīng)該去找出這個(gè)請(qǐng)求對(duì)應(yīng)的服務(wù)器。?

了解架構(gòu),必須要加上層次感?
同樣的,以爪哇寫(xiě)成的網(wǎng)頁(yè)應(yīng)用程式中,也許會(huì)應(yīng)用諸如Struts的之類(lèi)的的MVC框架,以及像Hibernate的這樣的資料存取框架。它們都可以視為最主要的架構(gòu)下的較次級(jí)架構(gòu)。而各個(gè)應(yīng)用系統(tǒng),甚至有可能在Struts的及休眠之下,建立自有的更次級(jí)的架構(gòu)。?

也就是說(shuō),當(dāng)我們談到“架構(gòu)”這樣的觀念時(shí),必須要有層次感。而不論是那一層級(jí)的架構(gòu),都會(huì)定義出各自的角色,以及角色間的關(guān)系。對(duì)閱讀者來(lái)說(shuō),相較于直接切入最細(xì)微的單一角色行為,不如了解某個(gè)特定的架構(gòu)中,究竟存在多少角色,以及這些角色之間的互動(dòng)模式,比較能夠幫助我們了解整個(gè)系統(tǒng)的運(yùn)作方式。?

這是一個(gè)很重要的關(guān)鍵,當(dāng)你試著進(jìn)到最細(xì)節(jié)處之前,應(yīng)該先試著找出參與的角色,及他們之間的關(guān)系。例如,對(duì)事件驅(qū)動(dòng)式的架構(gòu)而言,有3個(gè)很重要的角色。一個(gè)是事件處理的分派器(事件調(diào)度) ,一個(gè)是事件產(chǎn)生者(事件發(fā)生器) ,另一個(gè)則是事件處理器(事件處理程序) 。?

事件產(chǎn)生器產(chǎn)生事件,并送至事件分派器,而事件分派器負(fù)責(zé)找出各事件相對(duì)應(yīng)的事件處理器,并且轉(zhuǎn)交該事件,并命令事件處理器加以處理。像的圖形用戶(hù)界面的Windows應(yīng)用程式,便是采用事件驅(qū)動(dòng)式的架構(gòu)。?

當(dāng)你知道此類(lèi)的應(yīng)用程式皆為事件驅(qū)動(dòng)式的架構(gòu)時(shí),你便可以進(jìn)一步得知,在這樣的架構(gòu)下會(huì)有3種主要的角色。雖然也許還不清楚整個(gè)系統(tǒng)中,究竟會(huì)需要處理多少事件的類(lèi)型,但對(duì)你而言,已經(jīng)建立了對(duì)系統(tǒng)全貌最概觀的認(rèn)識(shí)。?

雖然你還不清楚所有的細(xì)節(jié),但諸如確切會(huì)有那些事件類(lèi)型之類(lèi)的資訊,在此刻還不重要─ ─不要忘了,我們采取的是由上而下的方式,要先摸清楚主建筑結(jié)構(gòu),至于壁紙的花色怎么處理,那是到了尾聲時(shí)才會(huì)做的事。?

探索架構(gòu)的第一件事:找出系統(tǒng)如何初始化?
有經(jīng)驗(yàn)的程式人,對(duì)于時(shí)常被運(yùn)用的架構(gòu)都很熟悉。常常只需要瞧上幾眼,就能明白一個(gè)系統(tǒng)所用的架構(gòu),自然就能夠直接聯(lián)想到其中會(huì)存在的角色,以及角色間的關(guān)系。然而,并不是每個(gè)系統(tǒng)所用的架構(gòu),都是大眾所熟悉,或是一眼能夠望穿的。這時(shí)候,你需要探索。目標(biāo)同樣要放在界定其中的角色,以及角色間的靜態(tài),動(dòng)態(tài)關(guān)系。?

不論某個(gè)系統(tǒng)所采用的架構(gòu)是否為大部分人所熟知的,在試著探索一個(gè)系統(tǒng)的長(zhǎng)相時(shí),我們應(yīng)該找出來(lái)幾個(gè)答案,了解在它所用的架構(gòu)下,下列這件事是如何被完成的:一,系統(tǒng)如何初始化,二,與這個(gè)系統(tǒng)相接的其他系統(tǒng)(或使用者)有那些,而相接的介面又是什么;三,系統(tǒng)如何反應(yīng)各種事件,四,系統(tǒng)如何處理各種異常及錯(cuò)誤。?

系統(tǒng)如何初始化是很重要的一件事,因?yàn)槌跏蓟菫榱私酉聛?lái)的所有事物而做的準(zhǔn)備。從初始化的方式,內(nèi)容,能知道系統(tǒng)做了什么準(zhǔn)備,對(duì)于系統(tǒng)會(huì)有什么行為展現(xiàn),也就能得窺一二了。之所以要了解與系統(tǒng)相接的其他系統(tǒng)(或使用者) ,為的是要界定出系統(tǒng)的邊界。其他的系統(tǒng)可能會(huì)提供輸入給我們所探索的系統(tǒng),也可能接收來(lái)自這系統(tǒng)的輸出,了解這邊界所在,才能確定系統(tǒng)的外觀。?

而系統(tǒng)所反應(yīng)的事件類(lèi)型,以及如何反應(yīng),基本上就代表著系統(tǒng)本身的主要行為模式。最后,我們必須了解系統(tǒng)處理異常及錯(cuò)誤的方式,這同樣也是系統(tǒng)的重要行為,但容易被忽略。之前,我們提到必須先具備一個(gè)系統(tǒng)的語(yǔ)言基礎(chǔ),才能夠進(jìn)一步加以閱讀,而在本文中,我們的重點(diǎn)放在:要了解一個(gè)系統(tǒng),最好是采取由上至下的方式。先試著捕捉系統(tǒng)架構(gòu)性的觀念,不要過(guò)早鉆進(jìn)細(xì)節(jié),因?yàn)槟峭ǔ?duì)于你了解全貌,沒(méi)有多大的幫助。




閱讀他人的程式碼( 3 ) -優(yōu)質(zhì)工具在手,讀懂程式非難事

系統(tǒng)的復(fù)雜度往往超過(guò)人腦的負(fù)荷。閱讀程式碼的時(shí)候,你會(huì)需要更多工具提供協(xié)助。使用好的整合式開(kāi)發(fā)環(huán)境( IDE )的或文字編輯器,就能提供最基本的幫助。

閱讀程式碼的動(dòng)作,可以是很原始的,利用最簡(jiǎn)單的文字編輯器,逐一開(kāi)啟原始碼,然后憑借著一己的組織能力,在不同的程式碼間跳躍,拼湊出腦中想要構(gòu)建的圖像。?
不過(guò),系統(tǒng)的復(fù)雜度往往超過(guò)人腦的負(fù)荷。閱讀程式碼的時(shí)候,你會(huì)需要更多工具提供協(xié)助。使用好的整合式開(kāi)發(fā)環(huán)境( IDE )的或文字編輯器,就能提供最基本的幫助。?

善用文字編輯器或IDE中,加速解讀程式碼?
許多文字編輯器提供了常見(jiàn)程式語(yǔ)言的語(yǔ)法及關(guān)鍵字標(biāo)示功能。這對(duì)于閱讀來(lái)說(shuō),絕對(duì)能夠起很大的作用。有些文字編輯器(例如我常用的編輯器及偶而使用的記事本+ + ) ,甚至能夠自動(dòng)列出某個(gè)原始檔中所有定義的函式清單,更允許你直接從清單中選擇函式,直接跳躍到該函式的定義位置。這對(duì)于閱讀程式碼的人來(lái)說(shuō),就提供了極佳的便利性。?

因?yàn)樵陂喿x程式碼時(shí),最常做的事,就是隨著程式中的某個(gè)控制流,將閱讀的重心,從某個(gè)函式移至它所呼叫的另一個(gè)函式。所以對(duì)程式人來(lái)說(shuō),閱讀程式碼時(shí)最常做的事之一就是:找出某個(gè)函式位在那一個(gè)原始檔里,接著找到該函式所在的位置。?

好的的IDE能夠提供的協(xié)助就更多了。有些能夠自動(dòng)呈現(xiàn)一些額外的資訊,最有用的莫過(guò)于函式的原型宣告了。例如,有些的IDE支援當(dāng)游標(biāo)停留在某函式名稱(chēng)上一段時(shí)間后,它會(huì)以提示的方式顯示該函式的原型宣告。?

對(duì)閱讀程式碼的人來(lái)說(shuō),在看到程式碼中呼叫到某個(gè)函式時(shí),可以直接利用這樣的支援,立即取得和這個(gè)函式有關(guān)的原型資訊,馬上就能知道呼叫該函式所傳入的各個(gè)引數(shù)的意義,而不必等到將該函式的定義位置找出后,才能明白這件事。?

grep按(讀者:推薦來(lái)源透視)是一個(gè)基本而極為有用的工具?
除了選用好的文字編輯器或的IDE之外,還有一個(gè)基本,但卻極為有用的工具,它就是grep按。熟悉的Unix作業(yè)系統(tǒng)的程式人,對(duì)grep按這個(gè)公用程式多半都不陌生。 grep按最大的用途,在于它允許我們搜尋某個(gè)目錄(包括遞回進(jìn)入所有子目錄)中所有指定檔案,是否有符合指定條件(常數(shù)字串或正規(guī)表示式)檔案。?

倘若有的話,則能幫你指出所在的位置。這在閱讀程式碼時(shí)的作用極大。當(dāng)我們隨著閱讀的腳步,遇上了任何一個(gè)不認(rèn)識(shí),但自認(rèn)為重要的類(lèi)別,函式,資料結(jié)構(gòu)定義或變數(shù),我們就得找出它究竟位在這茫茫程式碼海中的何處,才能將這個(gè)圖塊從未知變?yōu)橐阎?
grep按之所以好用,就是在于當(dāng)我們發(fā)現(xiàn)某個(gè)未知的事物時(shí),可以輕易地利用它找出這個(gè)未知的事物究竟位在何方。此外,雖說(shuō)grep按是Unix系統(tǒng)的標(biāo)準(zhǔn)公用程式之一,但是像視窗這樣子的平臺(tái),也有各種類(lèi)型的grep按程式。對(duì)于在視窗環(huán)境工作的程式人來(lái)說(shuō),可以自行選用覺(jué)得稱(chēng)手的工具。?

gtags可建立索引,讓搜尋更有效率?
grep按雖然好用,但是仍然有一些不足之處。第一個(gè)缺點(diǎn)在于它并不會(huì)為所搜尋的原始碼檔案索引。每當(dāng)你搜尋時(shí),它都會(huì)逐一地找出所有的檔案,并且讀取其中的所有內(nèi)容,過(guò)濾出滿(mǎn)足指定條件的檔案。當(dāng)專(zhuān)案的原始碼數(shù)量太大時(shí),就會(huì)產(chǎn)生搜尋效率不高的問(wèn)題。?

第二個(gè)缺點(diǎn)是它只是一個(gè)單純的文字檔搜尋工具,本身并不會(huì)剖析原始碼所對(duì)應(yīng)的語(yǔ)言語(yǔ)法。當(dāng)我們只想針對(duì)“函式”名稱(chēng)進(jìn)行搜尋時(shí),它有可能將注解中含有該名稱(chēng)的原始碼,也一并找了出來(lái)。?

針對(duì)grep按的缺點(diǎn),打算閱讀他人程式碼的程式人,可以考慮使用像是gtags這樣子的工具。 gtags是源代碼的GNU全局標(biāo)記系統(tǒng),它不只搜尋文字層次,而且因?yàn)榫邆淞烁鞣N語(yǔ)言的語(yǔ)法剖析器,所以在搜尋時(shí),可以只針對(duì)和語(yǔ)言有關(guān)的元素,例如類(lèi)別名稱(chēng),函式名稱(chēng)等。?

而且,它能針對(duì)原始碼的內(nèi)容進(jìn)行索引,這意謂一旦建好索引之后,每次搜尋的動(dòng)作,都毋需重新讀取所有原始碼的內(nèi)容并逐一搜尋。只需要以現(xiàn)成的索引結(jié)構(gòu)為基礎(chǔ),即可有效率的尋找關(guān)鍵段落。?

gtags提供了基于命令列的程式,讓你指定原始碼所在的目錄執(zhí)行建立索引的動(dòng)作。它同時(shí)也提供程式讓你得如同操作grep按一般,針對(duì)索引結(jié)構(gòu)進(jìn)行搜尋及檢索。它提供了許多有用的檢索方式,例如找出專(zhuān)案中定義某個(gè)資料結(jié)構(gòu)的檔案及定義所在的行號(hào),或者是找出專(zhuān)案中所有引用某資料結(jié)構(gòu)的檔案,以及引用處的行號(hào)。?

這么一來(lái),你就可以輕易地針對(duì)閱讀程式碼時(shí)的需求予以檢索。相較于grep按所能提供的支援, gtags這樣的工具,簡(jiǎn)直是強(qiáng)大許多。?

再搭配htags制作的HTML文件,更是如虎添翼?
還有一個(gè)絕對(duì)需要一提的工具。這個(gè)叫做htags的工具,能夠幫你將已制作完成的索引結(jié)構(gòu),制作成為一組相互參考的的HTML文件。基本上,利用這樣的的HTML文件閱讀程式碼,比起單純地直接閱讀原始碼,來(lái)得更有結(jié)構(gòu)。原因是閱讀程式碼時(shí),這樣的的HTML文件,已經(jīng)為你建立起在各個(gè)原始碼檔案片段間跳躍的鏈結(jié)。例如,圖一是針對(duì)一個(gè)有名的開(kāi)放原始碼專(zhuān)案ffmpeg ,由gtags所產(chǎn)生出來(lái)的的HTML文件首頁(yè)的一部分。?

?
htags工具首先為你找出所有定義的Main ( )函式的檔案,并且列出所在的函式。找出的Main ( )函式,時(shí)常是閱讀程式碼的第一步,因?yàn)橹饕?#xff08; )函式是程式的主要入口點(diǎn),所有的動(dòng)作皆由此啟動(dòng),它是一切事物的源頭。?
憑借htags制作的的HTML文件,你可以輕易地點(diǎn)擊超連結(jié),直接進(jìn)到的Main ( )函式所在的程式碼片段,如圖二。?

?

當(dāng)我們檢視上述原始碼時(shí),發(fā)現(xiàn)av_register_all ( )是個(gè)陌生,無(wú)法了解的事物,而想要搞懂它究竟是什么,可以再繼續(xù)點(diǎn)擊這個(gè)函式,如圖三。這真是太方便了!閱讀至此,你會(huì)猛然發(fā)現(xiàn), gtags仿佛就是為了閱讀程式碼而專(zhuān)門(mén)量身打造的利器。?




閱讀他人的程式碼( 4 ) -望文生義,進(jìn)而推敲組件的作用

先建立系統(tǒng)的架構(gòu)性認(rèn)識(shí),然后透過(guò)名稱(chēng)及命名慣例,就可以推測(cè)出各組件的作用。例如:當(dāng)AOL的Winamp嘗試著初始化一個(gè)插件時(shí),它會(huì)呼叫這個(gè)結(jié)構(gòu)中的初始化函式,以便讓每個(gè)插件程式有機(jī)會(huì)初始化自己。當(dāng)AOL的Winamp打算結(jié)束自己或結(jié)束某個(gè)插件的執(zhí)行時(shí),便會(huì)呼叫退出函式。

在閱讀程式碼的細(xì)節(jié)之前,我們應(yīng)先試著捕捉系統(tǒng)的運(yùn)作情境。在采取由上至下的方式時(shí),系統(tǒng)性的架構(gòu)是最頂端的層次,而系統(tǒng)的運(yùn)作情境,則是在它之下的另一個(gè)層次。

好的說(shuō)明文件難求,拼湊故事的能力很重要?
有些系統(tǒng)提供良善的說(shuō)明文件,也許還利用UML的充分描述系統(tǒng)的運(yùn)作情境。那么對(duì)于閱讀者來(lái)說(shuō),從系統(tǒng)的分析及設(shè)計(jì)文件著手,便是快速了解系統(tǒng)運(yùn)作情境的一個(gè)途徑。
但是,并不是每個(gè)軟體專(zhuān)案都伴隨著良好的系統(tǒng)文件,而許多極具價(jià)值的開(kāi)放原始碼專(zhuān)案,也時(shí)常不具備此類(lèi)的文件。對(duì)此,閱讀者必須嘗試自行捕捉,并適度地記錄捕捉到的運(yùn)作情境。?

我喜歡將系統(tǒng)的運(yùn)作情境,比擬成系統(tǒng)會(huì)上演的故事情節(jié)。在閱讀細(xì)節(jié)性質(zhì)的程式碼前,先知道系統(tǒng)究竟會(huì)發(fā)生那些故事,是必備的基本功課。你可以利用熟悉或者自己發(fā)明的表示工具,描述你所找到的情境。甚至可以只利用簡(jiǎn)單的列表,直接將它們列出。只要能夠達(dá)到記錄的目的,對(duì)程式碼閱讀來(lái)說(shuō),都能夠提供幫助。或者,你也可以利用基于UML中的類(lèi)別圖,合作圖,循序圖之類(lèi)的表示方法,做出更詳細(xì)的描述。?
當(dāng)你能夠列出系統(tǒng)可能會(huì)有的情境,表示你對(duì)系統(tǒng)所具備的功能,以及在各種情況下的反應(yīng),都具備概括性的認(rèn)識(shí)。以此為基礎(chǔ),便可在任何需要的時(shí)候,鉆進(jìn)細(xì)節(jié)處深入了解。?

探索架構(gòu)的第一步─ ─找到程式的入口?
在之前,我們?cè)谝粋€(gè)開(kāi)發(fā)專(zhuān)案中,曾經(jīng)需要將系統(tǒng)所得到的的MP3音訊檔,放至iPod的這個(gè)極受歡迎的播放設(shè)備中。?

雖然iPod的本身也可以做為可移動(dòng)式的儲(chǔ)存設(shè)備,但并不是單純地將MP3播放檔案放到中的iPod ,就可以讓蘋(píng)果的播放器認(rèn)得這個(gè)檔案,甚至能夠加以播放。?
這是因?yàn)樘O(píng)果利用一個(gè)特殊的檔案結(jié)構(gòu)( iTunes的數(shù)據(jù)庫(kù)) ,記錄播放器中可供播放的樂(lè)曲,播放清單以及樂(lè)曲資訊(例如專(zhuān)輯名稱(chēng),樂(lè)曲長(zhǎng)度,演唱者等) 。為了了解并且試著重復(fù)使用既有的程式碼,我們找到了一個(gè)AOL的Winamp的iPod的外掛程式(插件) 。?

AOL的Winamp是個(gè)人電腦上極受歡迎的播放軟體,而我們找到的外掛程式,能讓的軟件直接顯示連接至電腦的的iPod中的歌曲資訊,并且允許的軟件直接播放。?

我們追蹤與閱讀這個(gè)外掛程式的思路及步驟如下,首先,我們要先了解外掛程式的系統(tǒng)架構(gòu)。很明顯的,大概瀏覽過(guò)原始碼后,我們注意到它依循著AOL的Winamp為插件程式所制定的規(guī)范,也就是說(shuō),它是實(shí)作成的Windows上的DLL的,并且透過(guò)一個(gè)叫做winampGetMediaLibraryPlugin的DLL的函式,提供一個(gè)名為winampMediaLibraryPlugin的結(jié)構(gòu)。?
當(dāng)我們不清楚系統(tǒng)的架構(gòu)究竟為何時(shí),我們會(huì)試著探索,而第一步,便是找到程式的入口。如何找到呢?這會(huì)依程式的性質(zhì)不同而有所差別。?
對(duì)一個(gè)本身就是可獨(dú)立執(zhí)行的程式來(lái)說(shuō),我們會(huì)找啟動(dòng)程式的主要函式,例如對(duì)的C / C + +來(lái)說(shuō)就是主要( ) ,而對(duì)爪哇來(lái)說(shuō),便是靜無(wú)效的main ( ) 。在找到入口后,再逐一追蹤,摸索出系統(tǒng)的架構(gòu)。?
但有時(shí),我們所欲閱讀的程式碼是類(lèi)別庫(kù)或函式庫(kù),它只是用來(lái)提供多個(gè)類(lèi)別或函式供用戶(hù)端程式(客戶(hù)程序)使用,本身并不具單一入口,此類(lèi)的程式碼具有多重的入口─ ─每個(gè)允許用戶(hù)端程式呼叫的函式或類(lèi)別,都是它可能的入口。?

例如,對(duì)AOL的Winamp的iPod的插件來(lái)說(shuō),它是一個(gè)動(dòng)態(tài)鏈接庫(kù)形式的函式庫(kù),所以當(dāng)我們想了解它的架構(gòu)時(shí),必須要先找出它對(duì)外提供的函式,而對(duì)的Windows的DLL來(lái)說(shuō),對(duì)外提供的函式,皆會(huì)以dllexport這個(gè)關(guān)鍵字來(lái)修飾。所以,不論是利用grep按或gtags之類(lèi)的工具,我們可以很快從原始碼中,找到它只有一個(gè)DLL的函式(這對(duì)我們而言,真是一個(gè)好消息) ,而這個(gè)函式便是上述的winampGetMediaLibraryPlugin 。?

系統(tǒng)多會(huì)采用相同的架構(gòu)處理插件程式?
如果經(jīng)驗(yàn)不夠的話,也許無(wú)法直接猜出這個(gè)函式的作用。?
不過(guò),如果你是個(gè)有經(jīng)驗(yàn)的程式人,多半能從函式所回傳的結(jié)構(gòu),猜出這個(gè)函式實(shí)際的用途。而事實(shí)上,當(dāng)你已經(jīng)知道它是一個(gè)插件程式時(shí),就應(yīng)該要明白,它可能采用的,就是許多系統(tǒng)都采用的相同架構(gòu)處理插件程式。?

當(dāng)一個(gè)系統(tǒng)采用所謂插件形式的架構(gòu)時(shí),它通常不會(huì)知道它的插件究竟會(huì)怎么實(shí)作,實(shí)作什么功能。它只會(huì)規(guī)范插件程式需要滿(mǎn)足某個(gè)特定介面。當(dāng)系統(tǒng)初始化時(shí),所有的插件都可以依循相同的方式,向系統(tǒng)注冊(cè),合法宣示自己的存在。?

雖然系統(tǒng)并不確切知道插件會(huì)有什么行為展現(xiàn),但是因?yàn)樗贫艘粋€(gè)標(biāo)準(zhǔn)的介面,所以系統(tǒng)仍然可以預(yù)期每個(gè)插件能夠處理的動(dòng)作類(lèi)型。這些動(dòng)作具體上怎么執(zhí)行,對(duì)系統(tǒng)來(lái)說(shuō)并不重要。這也正是物件導(dǎo)向程式設(shè)計(jì)中的“多型”觀念。?

隨著實(shí)務(wù)經(jīng)驗(yàn),歸納常見(jiàn)的架構(gòu)模式?
我想表達(dá)的重點(diǎn),是當(dāng)你“涉世越深”之后,所接觸的架構(gòu)越多,就越能觸類(lèi)旁通。只需要瞧上幾眼,就能明白系統(tǒng)所用的架構(gòu),自然就能夠直接聯(lián)想到其中可能存在的角色,以及角色間的關(guān)系。?

像上述的插件程式手法,時(shí)常可以在許多允許“外掛”程式碼的系統(tǒng)中看到。所以,有經(jīng)驗(yàn)的閱讀者,多半能夠立即反應(yīng),知道像這樣的系統(tǒng)的軟件,應(yīng)該是讓每個(gè)插件程式,都寫(xiě)成DLL的函式庫(kù)。?

而每個(gè)插件的DLL的函式庫(kù)中,都必須提供winampGetMediaLibraryPlugin ( )這個(gè)函式(如果你熟悉的Windows的程式設(shè)計(jì),你會(huì)知道這是利用加載( )和GetProcAddress ( )來(lái)達(dá)成的一種多型手法) 。如果你熟悉設(shè)計(jì)模式,你更會(huì)知道這是簡(jiǎn)單工廠方法這個(gè)設(shè)計(jì)模式的運(yùn)用。?
winampGetMediaLibraryPlugin ( )所回傳的winampMediaLibraryPlugin結(jié)構(gòu),正好就描述了每個(gè)AOL的Winamp插件的實(shí)作內(nèi)容。?

善用名稱(chēng)可加速了解?
利用gtags這個(gè)工具,我們立即發(fā)現(xiàn),這個(gè)插件它所定義的初始化,退出, PluginMessageProc這三個(gè)名稱(chēng),都是函式名稱(chēng)。這暗示在多型的作用下,它們都是在某些時(shí)間點(diǎn),會(huì)由AOL的Winamp核心本體呼叫的函式。?

名稱(chēng)及命名慣例是很重要的。看到“初始化” ,我們會(huì)知道它的作用多半是進(jìn)行初始化的動(dòng)作,而“退出”大概就是結(jié)束時(shí)處理函式,而PluginMessageProc多半就是各種訊息的處理常式(過(guò)程通常是程序的簡(jiǎn)寫(xiě),所以PluginMessageProc意指插件訊息程序)了。?

“望文生義”很重要,我們看到函式的名稱(chēng),就可以猜想到它所代表的作用,例如:當(dāng)AOL的Winamp嘗試著初始化一個(gè)插件時(shí),它會(huì)呼叫這個(gè)結(jié)構(gòu)中的初始化函式,以便讓每個(gè)插件程式有機(jī)會(huì)初始化自己;當(dāng)AOL的Winamp打算結(jié)束自己或結(jié)束某個(gè)插件的執(zhí)行時(shí),便會(huì)呼叫退出函式。當(dāng)AOL的Winamp要和插件程式溝通時(shí),它會(huì)發(fā)送各種不同的訊息至插件,而插件程式必須對(duì)此做出回應(yīng)。?

我們甚至不需要檢視這幾個(gè)函式的內(nèi)容,就可以做出推測(cè),而這樣的假設(shè),事實(shí)上也是正確的。




閱讀他人的程式碼( 5 ) -找到程式入口,再由上而下抽絲剝繭

根據(jù)需要決定展開(kāi)的層數(shù),或展開(kāi)特定節(jié)點(diǎn),并記錄樹(shù)狀結(jié)構(gòu),然后適度忽略不需要了解的細(xì)節(jié)─這是一個(gè)很重要的態(tài)度。因?yàn)槟悴粫?huì)一次就需要所有的細(xì)節(jié),閱讀都是有目的的,每次的閱讀也許都在探索程式中不同的區(qū)域。

探索系統(tǒng)架構(gòu)的第一步,就是找到程式的入口點(diǎn)。找到入口點(diǎn)后,多半采取由上而下(自上而下)的方式,由最外層的結(jié)構(gòu),一層一層逐漸探索越來(lái)越多的細(xì)節(jié)。?
我們的開(kāi)發(fā)團(tuán)隊(duì)曾針對(duì)AOL的Winamp的iPod的插件進(jìn)行閱讀及探索,不僅找到入口點(diǎn),也找出,并理解它最根本的基礎(chǔ)架構(gòu)。從這個(gè)入口點(diǎn),可以往下再展開(kāi)一層,分別找到三個(gè)重要的組成及其意義:?
●的init ( ) :初始化動(dòng)作?
?????●退出( ) :終止化動(dòng)作?
?????● PluginMessageProc ( ) :以訊息的方式處理程式所必須處理的各種事件

展開(kāi)的同時(shí),隨手記錄樹(shù)狀結(jié)構(gòu)?
當(dāng)我們從一個(gè)入口點(diǎn)找到三個(gè)分支后,可以順著每個(gè)分支再展開(kāi)一層,所以分別繼續(xù)閱讀的init ,退出,以及PluginMessageProc的內(nèi)容,并試著再展開(kāi)一層。閱讀的同時(shí),你可以在文件中試著記錄展開(kāi)的樹(shù)狀結(jié)構(gòu)。?
●的init ( ) :初始化動(dòng)作?
?????● itunesdb_init_cc ( ) :建立存取iTunes的數(shù)據(jù)庫(kù)的同步物件?
?????●初始化資料結(jié)構(gòu)?
?????●初始化的GUI元素?
?????●載入設(shè)定?
?????●建立日志檔?
?????● autoDetectIpod ( ) :偵測(cè)的iPod插入的執(zhí)行緒?
?????●退出( ) :終止化動(dòng)作?
?????● itunesdb_del_cc ( ) :終止存取iTunes的數(shù)據(jù)庫(kù)的同步物件?
?????●關(guān)閉日志檔?
?????●終止化圖形用戶(hù)界面元素?
?????● PluginMessageProc ( ) :以訊息的方式處理程式所必須面臨的各種事件
?????●執(zhí)行所連接之蘋(píng)果的MessageProc ( )?

這部分必須要留意幾個(gè)重點(diǎn)。首先,應(yīng)該一邊閱讀,一邊記錄文件。因?yàn)槿说挠洃浟νǔS邢?#xff0c;對(duì)于陌生的事物更是容易遺忘,因此邊閱讀邊記錄,是很好的輔助。?
再者,因?yàn)槲覀儾扇∮缮隙碌姆绞?#xff0c;從一個(gè)點(diǎn)再分支出去成為多個(gè)點(diǎn),因此,通常也會(huì)以樹(shù)狀的方式記錄。除此之外,每次只試著往下探索一層。從的init ( )來(lái)看你便會(huì)明白。?

以下試著摘要的init ( )的內(nèi)容:?
詮釋的init ( ) (?
itunesdb_init_cc ( ) ;?
currentiPod =空;?
蘋(píng)果=新C_ItemList ;?
...略?
conf_file = (字符* ) SendMessage?
( plugin.hwndWinampParent , WM_WA_IPC , 0 , IPC_GETINIFILE ) ;?
m_treeview = GetDlgItem ( plugin.hwnd LibraryParent , 0x3fd ) ;?
/ /這個(gè)數(shù)字實(shí)際上是魔術(shù): )?
...略?
g_detectAll = GetPrivateProfileInt ( “ ml_ipod ” , “ detectAll ” , 0 , conf_file ) ! = 0 ;?
...略?
g_log = GetPrivateProfileInt ( “ ml_ipod ” , “日志” , 0 , conf_file ) ! = 0 ;?
...略?
g_logfile =打開(kāi)( g_logfilepath ,有“ A ” ) ;?
...略?
autoDetectIpod ( ) ;?
返回0 ;?
)?

因?yàn)槲覀冎辉囍嗵剿饕粚?#xff0c;而目的是希望發(fā)掘出下一層的子動(dòng)作。所以在的init ( )中看到像“ itunesdb_init_cc ( ) ; ”這樣的函式呼叫動(dòng)作時(shí),我們知道它是在初始化( )之下的一個(gè)獨(dú)立子動(dòng)作,所以可以直接將它列入。但是當(dāng)看到如下的程式行:?
currentiPod =空;?
蘋(píng)果=新C_ItemList ;?

我們并不會(huì)將它視為的init ( )下的一個(gè)獨(dú)立的子動(dòng)作。因?yàn)楹脦仔谐淌?#xff0c;才構(gòu)成一個(gè)具有獨(dú)立抽象意義的子動(dòng)作。例如以上這兩行構(gòu)成了一個(gè)獨(dú)立的抽象意義,也就是初始化所需的資料結(jié)構(gòu)。?

理論上,原來(lái)的程式撰寫(xiě)者,有可能撰寫(xiě)一個(gè)叫做init_data_structure ( )的函式,包含這兩行程式碼。這樣做可讀性更高,然而基于種種理由,原作者并沒(méi)有這么做。身為閱讀者,必須自行解讀,將這幾行合并成單一個(gè)子動(dòng)作,并賦予它一個(gè)獨(dú)立的意義─ ─初始化資料結(jié)構(gòu)。?

無(wú)法望文生義的函式,先試著預(yù)看一層?
對(duì)于某些不明作用的函式叫用,不是望其文便能生其義的。當(dāng)我們看到“ itunesdb_init_cc ( ) ”這個(gè)名稱(chēng)時(shí),我們或許能從“ itunesdb_init ”的字眼意識(shí)到這個(gè)函式和蘋(píng)果所采用的的iTunes數(shù)據(jù)庫(kù)的初始化有關(guān),但“循環(huán)”卻實(shí)在令人費(fèi)解。為了理解這一層某個(gè)子動(dòng)作的真實(shí)意義,有時(shí)免不了要往前多看一層。?

原來(lái)它是用來(lái)初始化同步化機(jī)制用的物件。作用在于這程式一定是用了某個(gè)內(nèi)部的資料結(jié)構(gòu)來(lái)儲(chǔ)存的iTunes數(shù)據(jù)庫(kù),而這資料結(jié)構(gòu)有可能被多執(zhí)行緒存取,所以必須以同步物件(此處是視窗的臨界區(qū))加以保護(hù)。?

所以說(shuō),當(dāng)我們?cè)囍詷?shù)狀的方式,逐一展開(kāi)每個(gè)動(dòng)作的子動(dòng)作時(shí),有時(shí)必須多看一層,才能真正了解子動(dòng)作的意義。因?yàn)橛辛诉@樣的動(dòng)作,我們可以在展開(kāi)樹(shù)狀結(jié)構(gòu)中,為itunesdb_init_cc ( )附上補(bǔ)充說(shuō)明:建立存取iTunes的數(shù)據(jù)庫(kù)的同步物件。這么一來(lái),當(dāng)我們?cè)跈z視自己所寫(xiě)下的樹(shù)狀結(jié)構(gòu)時(shí),就能輕易一目了然的理解每個(gè)子動(dòng)作的真正作用。?

根據(jù)需要了解的粒度,決定展開(kāi)的層數(shù)?
我們究竟需要展開(kāi)多少層呢?這個(gè)問(wèn)題和閱讀程式碼時(shí)所需的“粒度(粒度) ”有關(guān)。如果我們只是需要概括性的了解,那么也許展開(kāi)兩層或三層,就能夠?qū)Τ淌接谢A(chǔ)的認(rèn)識(shí)。倘若需要更深入的了解,就會(huì)需要展開(kāi)更多的層次才行。?

有時(shí)候,你并不是一視同仁地針對(duì)每個(gè)動(dòng)作,都展開(kāi)到相同深度的層次。也許,你會(huì)基于特殊的需求,專(zhuān)門(mén)針對(duì)特定的動(dòng)作展開(kāi)至深層。例如,我們閱讀AOL的Winamp的iPod插件的程式目錄,其實(shí)是想從中了解究竟應(yīng)該如何存取的iPod上的iTunes的數(shù)據(jù)庫(kù),使我們能夠?qū)P3播放歌曲或播放清單加至此數(shù)據(jù)庫(kù)中,并于的iPod中播放。?

當(dāng)我們層層探索與分解之后,找到了parseIpodDb ( ) ,從函式名稱(chēng)判斷它是我們想要的。因?yàn)樗淼恼瞧饰鰅Pod的數(shù)據(jù)庫(kù),正是我們此次閱讀的重點(diǎn),也就達(dá)成閱讀這程式碼的目的。?

我們強(qiáng)調(diào)一種不同的做法:在閱讀程式碼時(shí),多半采取由上而下的方式,而本文建議了一種記錄閱讀的方式,就是試著記錄探索追蹤時(shí)層層展開(kāi)的樹(shù)狀結(jié)構(gòu)。你可以視自己需要,了解的深入程度,再?zèng)Q定要展開(kāi)的層數(shù)。你更可以依據(jù)特殊的需要,只展開(kāi)某個(gè)特定的節(jié)點(diǎn),以探索特定的細(xì)目。?

適度地忽略不需要了解的細(xì)節(jié),是一個(gè)很重要的態(tài)度,因?yàn)槟悴粫?huì)一次就需要所有的細(xì)節(jié),閱讀都是有目的的。每次的閱讀也許都在探索程式中不同的區(qū)域;而每次探索時(shí),你都可以增補(bǔ)樹(shù)狀結(jié)構(gòu)中的某個(gè)子結(jié)構(gòu)。漸漸地,你就會(huì)對(duì)這個(gè)程式更加的了解。




閱讀他人的程式碼( 6 ) -閱讀的樂(lè)趣:透過(guò)程式碼認(rèn)識(shí)作者

即便每個(gè)人的寫(xiě)作模式多半受到他人的影響,程式人通常還是會(huì)融合多種風(fēng)格,而成為自己獨(dú)有的特色,如果你知道作者程式設(shè)計(jì)的偏好,閱讀他的程式碼就更得心應(yīng)手。

閱讀程式碼時(shí),多半會(huì)采取由上而下,抽絲剝繭的方式。透過(guò)記錄層層展開(kāi)的樹(shù)狀結(jié)構(gòu),程式人可以逐步地建立起對(duì)系統(tǒng)的架構(gòu)觀,而且可以依照需要的粒度(粒度) ,決定展開(kāi)的層次及精致程度。?

建立架構(gòu)觀點(diǎn)的認(rèn)識(shí)是最重要的事情。雖然這一系列的文章前提為“閱讀他人的程式碼” ,但我們真正想做的工作,并不在于徹底地詳讀每一行程式碼的細(xì)節(jié),而是想要透過(guò)重點(diǎn)式的程式碼“摘讀” ,達(dá)到對(duì)系統(tǒng)所需程度的了解。每個(gè)人在閱讀程式碼的動(dòng)機(jī)不盡相同,需要了解的程度也就有深淺的分別。只有極為少數(shù)的情況下,你才會(huì)需要細(xì)讀每一行程式碼。?

閱讀程式碼是新時(shí)代程式人必備的重要技能?
這一系列的文章至此已近尾聲,回顧曾探討的主題,我們首先研究了閱讀程式碼的動(dòng)機(jī)。尤其在開(kāi)放原始碼的風(fēng)氣如此之盛的情況下,妥善利用開(kāi)放原始碼所提供的資源,不僅能夠更快學(xué)習(xí)到新的技術(shù),同時(shí)在原始碼版權(quán)合適時(shí),還可以直接利用現(xiàn)成的程式碼,大幅地提高開(kāi)發(fā)階段的生產(chǎn)力。所以,閱讀程式碼儼然成為了新時(shí)代程式人必備的重要技能之一。?
接著,我們提到了閱讀程式碼前的必要準(zhǔn)備,包括了對(duì)程式語(yǔ)言,命名慣例的了解等等。在此之后,我們反覆提起了“由上而下”的閱讀方向的重要性。?
由上而下的閱讀方式,是因?yàn)槲覀冎匾暭軜?gòu)更勝于細(xì)節(jié)。從最外層的架構(gòu)逐一向內(nèi)探索,每往內(nèi)探索一層,我們了解系統(tǒng)的粒度就增加了一個(gè)等級(jí)。當(dāng)你識(shí)別出系統(tǒng)所用的架構(gòu)時(shí),便能夠輕易了解在這個(gè)架構(gòu)下會(huì)有的角色,以及它們之間的動(dòng)態(tài)及靜態(tài)的關(guān)系。如此一來(lái),許多資訊便不言可喻,毋需額外花費(fèi)力氣,便能夠快速理解。?

好的名稱(chēng)能夠摘要性地點(diǎn)出實(shí)體的作用?
追蹤原始碼時(shí),固然可以用本來(lái)的方式,利用編輯器開(kāi)啟所需的檔案,然后利用編輯器提供的機(jī)制閱讀,但是倘若能夠善用工具,閱讀程式碼的效率及品質(zhì)都能大大提升。在本系列文章中,我們介紹了一些工具,或許你還可以在坊間找到其他更有用的工具。?
我在這一系列的文章中,實(shí)際帶著大家閱讀,追蹤了一個(gè)名為ml_pod的開(kāi)放原始碼專(zhuān)案。它是一個(gè)AOL的Winamp的iPod的外掛程式。在追蹤的過(guò)程中,我們?cè)囍∽C這一系列文中所提到的觀念及方法。我們采用逐漸開(kāi)展的樹(shù)狀結(jié)構(gòu)來(lái)記錄追蹤的過(guò)程,并借以建立起對(duì)系統(tǒng)的概觀認(rèn)識(shí)。?

就原始碼的閱讀來(lái)說(shuō),之前的討論涉及了工具面及技巧面。但還有一些主題不在這兩個(gè)范疇之內(nèi),例如,善用名稱(chēng)賦予你的提示。名稱(chēng)做為隱喻(隱喻)的作用很大,好的名稱(chēng)能夠摘要性地點(diǎn)出實(shí)體的作用,例如我們看到autoDetectIpod ( ) ,自然而然能夠想像它的作用在于自動(dòng)(自動(dòng))偵測(cè)(檢測(cè))的iPod的存在。?

我們?cè)谡归_(kāi)樹(shù)狀結(jié)構(gòu)時(shí),有時(shí)候需要預(yù)看一層,有時(shí)卻不需要這么做,便可得到印證。程式人都會(huì)有慣用的名稱(chēng)以及組合名稱(chēng)的方法,倘若能夠從名稱(chēng)上理解,便毋需鉆進(jìn)細(xì)節(jié),可以省去相當(dāng)多的時(shí)間。例如,當(dāng)我們看到parseIpodDb ( )時(shí),便可以輕易了解它是剖析(解析)的iPod的資料庫(kù)( DB )的,因此便不需要立即鉆進(jìn)parseIpodDb ( )中查看底細(xì)。?

盡管如此,能否理解程式人命名的用意,和自身的經(jīng)驗(yàn)以及是否了解原作者的文化背景,是息息相關(guān)的。?

命名本身就是一種文化產(chǎn)物。不同的程式人文化,就會(huì)衍生出不同的命名文化。當(dāng)你自己的經(jīng)驗(yàn)豐富,看過(guò)及接觸過(guò)的程式碼也多時(shí),對(duì)于名稱(chēng)的感受及聯(lián)想的能力自然會(huì)有不同。?

這種感受和聯(lián)想的能力,究竟應(yīng)該如何精進(jìn),很難具體描述。就我個(gè)人的經(jīng)驗(yàn),多觀察不同命名體系的差異,并且嘗試歸納彼此之間的異同,有助于更快地提升對(duì)名稱(chēng)的感受及聯(lián)想力。?

轉(zhuǎn)換立場(chǎng),理解作者的思考方式?
除了工具及技巧之外, “想要閱讀程式碼,得先試著閱讀寫(xiě)這個(gè)程式碼的程式人的心。 ”這句話說(shuō)來(lái)十分抽象,或許也令人難以理解。?

當(dāng)你在閱讀一段程式碼時(shí),或許可以試著轉(zhuǎn)換自己的立場(chǎng),從旁觀者的角度轉(zhuǎn)換成為寫(xiě)作者的心態(tài),揣摩原作者的心理及處境。當(dāng)你試著設(shè)身處地站在他的立場(chǎng),透過(guò)他的思考方式來(lái)閱讀,追蹤他所寫(xiě)下的程式碼,將會(huì)感覺(jué)更加流暢。?

許多軟體專(zhuān)案,都不是由單一程式人所獨(dú)力完成。因此,在這樣的專(zhuān)案中,便有可能呈現(xiàn)多種不同的風(fēng)格。?

許多專(zhuān)案會(huì)由架構(gòu)師決定主體的架構(gòu)及運(yùn)作,有既定實(shí)施的命名慣例,及程式設(shè)計(jì)需要遵守方針。在多人開(kāi)發(fā)的模式下,越是好的軟體專(zhuān)案,越看不出某程式碼片段究竟是由誰(shuí)所寫(xiě)下的。?

不過(guò),有些開(kāi)放原始碼的專(zhuān)案,往往又整合了其他開(kāi)放原始碼的專(zhuān)案。有的時(shí)候,也很難求風(fēng)格的統(tǒng)一,便會(huì)出現(xiàn)混雜的情況。好比之前提到的ml_pod專(zhuān)案,因?yàn)槌淌酱a中混合了不同的來(lái)源,而呈現(xiàn)風(fēng)格不一致的情況。?

我在閱讀非自己所寫(xiě)的程式碼時(shí),會(huì)觀察原作者寫(xiě)作的習(xí)慣,借以對(duì)應(yīng)到腦中所記憶的多種寫(xiě)作模型。在閱讀的過(guò)程中,讀完幾行程式碼,我會(huì)試著猜想原作者在寫(xiě)下這段程式碼時(shí)的心境。他寫(xiě)下這段程式碼的用意是什么?為什么他會(huì)采取這樣的寫(xiě)法?順著原作者的思考理路閱讀,自己的思考才能更貼近對(duì)方寫(xiě)作當(dāng)時(shí)的想法。?

當(dāng)你短暫化身為原作者時(shí),才能更輕易的理解他所寫(xiě)下的程式碼。?
如果你能知道原作者的背景,程式設(shè)計(jì)時(shí)的偏好,閱讀他的程式碼,就更能得心應(yīng)手了。?

從程式碼著手認(rèn)識(shí)作者獨(dú)有的風(fēng)格,進(jìn)而見(jiàn)賢思齊?
我在閱讀別人寫(xiě)下的程式碼時(shí),我會(huì)試著猜想,原作者究竟是屬于那一種“流派”呢?每個(gè)人都有自己獨(dú)特的寫(xiě)作模式,即便每個(gè)人的寫(xiě)作模式多半受到他人的影響─ ─不論是書(shū)籍的作者,學(xué)習(xí)過(guò)程中的指導(dǎo)者,或一同參與專(zhuān)案的同儕,但每個(gè)程式人通常會(huì)融合多種風(fēng)格,而成為自己獨(dú)有的風(fēng)格。?

物件導(dǎo)向的基本教義派,總是會(huì)以他心中覺(jué)得最優(yōu)雅的物件導(dǎo)向方式來(lái)撰寫(xiě)程式。而閱讀慣用,善用設(shè)計(jì)模式的程式人所寫(xiě)下的程式碼時(shí),不難推想出他會(huì)在各種常見(jiàn)的應(yīng)用情境下,套用哪些模式。?

有些時(shí)候,在閱讀之初,你并不知道原作者的習(xí)性跟喜好,甚至你也不知道他的功力。但是,在閱讀之后,你會(huì)慢慢地從一個(gè)程式人所寫(xiě)下的程式碼,開(kāi)始認(rèn)識(shí)他。?

你或許會(huì)在閱讀他人的程式碼時(shí),發(fā)現(xiàn)令人拍案叫絕的技巧或設(shè)計(jì)。你也有可能在閱讀的同時(shí),發(fā)現(xiàn)原作者所留下的缺失或?qū)懽鲿r(shí)的缺點(diǎn),而暗自警惕于心。這也算是閱讀他人程式碼時(shí)的一項(xiàng)樂(lè)趣。?

當(dāng)你從視閱讀他人的程式碼為畏途,轉(zhuǎn)變成為可以從中獲取樂(lè)趣的時(shí)候,我想,你又進(jìn)到了另一個(gè)境界。

轉(zhuǎn)載于:https://www.cnblogs.com/DeasonGuan/archive/2011/07/14/2106687.html

總結(jié)

以上是生活随笔為你收集整理的如何看懂源代码--(分析源代码方法)(转)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。

主站蜘蛛池模板: 精品欧美视频 | 国产a黄 | 国产专区欧美专区 | 毛片在哪里看 | 99国产精 | www.亚洲成人| 久久久久国产精品无码免费看 | 亚洲天堂社区 | 在线爽 | www.插插插 | 色欲久久久天天天精品综合网 | 精品国产xxx| 日本成人在线视频网站 | wwwjizzzcom| 国产在线观看免费av | 国产伦精品一区二区三区妓女下载 | www午夜视频 | 69精品久久 | 美女被草出白浆 | 亚洲一级特黄毛片 | 男人的天堂视频 | 天天舔天天射天天干 | 夜夜欢天天干 | 伊人影视网 | 天天射天天舔 | 麻豆一区二区 | 999这里只有精品 | 久久亚 | 波多野结衣欲乱上班族 | 精品少妇3p | 亚洲片在线观看 | 亚洲手机视频 | 奇米四色影视 | 国产稀缺真实呦乱在线 | 伊在线久久丫 | 久久新网址 | 亚洲一区视频在线 | 欧美一区二区影院 | 欧美日韩一级二级 | 国产福利不卡视频 | 少妇高潮久久久久久潘金莲 | 五月天激情开心网 | 久久久久草| 亚洲精品88 | 四虎少妇做爰免费视频网站四 | 可以在线看黄的网站 | 欧洲精品久久久久毛片完整版 | 色五丁香 | 色哟哟网站在线观看 | 精品一性一色一乱农村 | 国产特级黄色录像 | 国产又粗又猛又爽又黄的视频在线观看动漫 | 亚洲一二三区在线 | 无人在线观看高清视频 | 亚洲欧洲另类 | 裸体女视频 | www.五月天婷婷 | 中文字幕一区二区人妻在线不卡 | 亚洲精品www久久久久久 | 日本理伦片午夜理伦片 | www噜噜噜 | 1024亚洲| 超碰女人| 欧美日韩少妇精品 | 青青操影院 | 99热18| 日韩a级大片 | 国产精品久久久久久久妇 | 久久综合日本 | 色一情一区二区三区四区 | 成人午夜激情影院 | 日韩欧美一区二区视频 | 亚洲AV午夜福利精品一级无码 | 亚洲 自拍 另类 欧美 丝袜 | 久久aaa| 天堂8中文 | 黑丝袜av | 天天摸日日 | 蜜桃成人在线观看 | 亚洲图片欧美视频 | 无码人妻黑人中文字幕 | 久久久久99精品成人片 | www激情com | 亚洲精品国产日韩 | 爱爱短视频 | 毛片无遮挡| 亚洲精品丝袜 | 欧美成人国产 | 一区二区三区四区在线免费观看 | 日韩黄视频 | 色av综合网 | 国产操视频 | 动漫美女隐私无遮挡 | 青青久久久 | 在线免费观看av不卡 | 天堂av在线免费观看 | 麻豆精品国产传媒 | 第四色在线视频 | 播放一级黄色片 |