做网站一般多少,印刷厂网站建设,企业咨询服务公司,西安网站设计制作一般多少钱我们在写程式时#xff0c;有不少时间都是在看别人的代码。例如看小组的代码#xff0c;看小组整合的守则#xff0c;若一开始没规划怎么看#xff0c; 就会“噜看噜苦#xff08;台语#xff09; ” 不管是参考也好#xff0c;从开源抓下来研究也好#xff0c;为了了解… 我们在写程式时有不少时间都是在看别人的代码。例如看小组的代码看小组整合的守则若一开始没规划怎么看 就会“噜看噜苦台语 ” 不管是参考也好从开源抓下来研究也好为了了解箇中含意在有限的时间下不免会对庞大的源代码解读感到压力。网路上有一篇关于分析看代码的方法做为程式设计师的您不妨参考看看换个角度来分析。 也能更有效率的解读你想要的程式码片段。 六个章节 1 读懂程式码使心法皆为我所用。 2 摸清架构便可轻松掌握全貌。 3 优质工具在手读懂程式非难事。 4 望文生义进而推敲组件的作用。 5 找到程式入口再由上而下抽丝剥茧。 6 阅读的乐趣透过程式码认识作者。 阅读他人的程式码 1 —读懂程式码使心法皆为我所用 程式码是别人写的只有原作者才真的了解程式码的用途及涵义。许多程式人心里都有一种不自觉的恐惧感深怕被迫去碰触其他人所写的程式码。但是与其抗拒接收别人的程式码不如彻底了解相关的语言和惯例当成是培养自我实力的基石。 对大多数的程式人来说撰写程式码或许是令人开心的一件事情但我相信有更多人视阅读他人所写成的程式码为畏途。许多人宁可自己重新写过一遍程式码也不愿意接收别人的程式码进而修正错误维护它们甚至加强功能。 这其中的关键究竟在何处呢若是一语道破其实也很简单程式码是别人写的只有原作者才真的了解程式码的用途及涵义。许多程式人心里都有一种不自觉的恐惧感深怕被迫去碰触其他人所写的程式码。这是来自于人类内心深处对于陌生事物的原始恐惧。 读懂别人写的程式码让你收获满满 不过基于许多现实的原因程式人时常受迫要去接收别人的程式码。例如同事离职了必须接手他遗留下来的工作也有可能你是刚进部门的菜鸟而同事经验值够了升级了风水轮流转一代菜鸟换菜鸟。甚至你的公司所承接的专案必须接手或是整合客户前一个厂商所遗留下来的系统你们手上只有那套系统的原始码运气好时还有数量不等的文件 。 诸如此类的故事其实时常在程式人身边或身上持续上演着。许多程式人都将接手他人的程式码当做一件悲惨的事情。每个人都不想接手别人所撰写的程式码因为不想花时间去探索宁可将生产力花在产生新的程式码而不是耗费在了解这些程式码上。 很遗憾的是上述的情况对程式人来说很难避免。我们总是必须碰触到其他人所写成的程式码甚至必须了解它加以修改。对于这项需求在现今开放原始码的风气如此盛行的今日正如之前的“程式设计2.0 ”文中所提到的你可以透过开放原始码学习到新的技术学习到高手的架构设计大幅提高学习的效率及效果。你甚至可以直接自开放原始码专案中抽取提炼出自己所需的程式码站在巨人的肩膀上直接由彼端获得所需的生产力。从这个观点来看读懂别人所写的程式码就不再只是从负面观点的“被迫接收” 而是极具正面价值的“汲取养份。 ” 先了解系统架构与行为模式再细读 倘若撰写程式码是程式人的重要技艺之一那么读懂别人的程式码接着加以修改也势必是另一个重要的技艺。 如果你不能熟悉这项工作不仅在遭逢你所不愿面对的局面时无法解决眼前接手他人程式码的难题更重要的是当你看着眼前现成的程式码却不知如何从中撷取自己所需导致最后只能入宝山空手回望之兴叹。 接触他人的程式码大致上可以分为三种程度一了解二修改扩充三抽取提炼。了解别人的程式码是最基础的工作倘若不能了解自己要处理的程式码就甭论修改或扩充更不可能去芜存菁从中萃取出自己所需回收再利用别人所撰写的程式码。虽说是“阅读” 但程式码并不像文章或小说一样透过这种做法便能够获得一定程度的了解。阅读文章或小说时几乎都是循序地阅读你只消翻开第一页一行行阅读下去即可。但是有许多程式人在试着阅读其他人的程式码时却往往有不知如何读起的困难。 或许找到系统的第一页也就是程式码执行的启始点并不难但是复杂度高的系统有时十分庞大有时千头万绪。 从程式码的启始点开始读起一来要循序读完所有的程式码旷日费时二来透过这种方式来了解系统很难在脑中构建出系统的面貌进而了解到系统真正的行为。所以阅读程式码的重点不在于读完每一行程式码而是在于有效率地透过探索及阅读从而了解系统的架构及行为模式。以便在你需要了解任何片段的细节实作时能够很快在脑上对映到具体的程式码位置直到那一刻才是细读的时机。 熟悉沟通语言与惯例用语 不论如何有些基本的准备是阅读他人程式码时必须要有的。 首先你最好得了解程式码写成的程式语言。想要读懂法文写成的小说总不能连法文都不懂吧。有些情况则很特殊。我们虽然不懂该程式码撰写所用的语言但是因为现代语言的高阶化而且流行的程式语言多半都是血统相近所以即使不那么熟悉有时也可勉力为之。 除了认识所用语言之外再来就是要先确认程式码所用的命名惯例命名惯例 。了解命名惯例很重要不同的程式人或开发团队差异可能很大。 这命名惯例涵盖的范围通常包括了变数的名称函式的名称类别如果是物件导向的话的名称原始码档案甚至是专案建构目录的名称。倘若使用了像设计模式之类的方法这些名称更有一些具体的表述方式。 命名惯例有点像是程式人在程式语言之上另行建构的一组沟通行话。程式人会透过共通约束遵守的命名惯例来表达一些较高阶的概念。例如有名的匈牙利式命名法便将变数名称以属性型别说明合并在一起描述。对程式人来说这种方式能够提供更丰富的资讯以了解该变数的作用及性质。 对程式码阅读来说熟悉这个做法之所以重要是因为当你了解整个系统所采用的惯例时你便能试着以他们所共同操用的语汇来进行理解。倘若不能了解其所用的惯例那么这些额外提供的资讯就无法为你所用。像以设计模式写成的程式码同样处处充满着模式的名称诸如工厂门面代理等等。以这些名称指涉的类别也直接透过名称表达了它们自身的作用。对于懂得这命名惯例的读者来说不需要深入探索也能很快捕捉到这些类别的意义。 当你拿到一套必须阅读的程式码时最好先取得命名惯例的说明文件。然而并不是每套程式码都附有此类的说明文件。另一个方式就是自己到程式码中大略浏览一遍有经验的程式人可以轻易发掘出该系统所用的命名惯例。 常见的命名方式不脱那几类这时候经验就很重要倘若你知道的惯例越多就越能轻易识别他人所用的惯例。如果运气很糟程式码所用的惯例是前所未见的那么你也得花点时间归纳凭自己的力量找出这程式码命名上的规则。 掌握程式码撰写者的心态与习惯 大多数的程式码基本上都依循一致的命名惯例。不过运气更差的时候一套系统中可能会充斥着多套命名惯例。这有可能是因为开发团队由多组人马所构成每组人马都有不同的文化而在专案开发管理又没有管控得宜所造成。最糟的情况程式码完全没有明显的惯例可言这时候阅读的难度就更高了。 想要阅读程式码得先试着体会程式码作者的“心” 。想要这么做就得多了解对方所使用的语言以及惯常运用的语汇。在下一回中我们将继续探讨阅读程式码的相关议题。 阅读他人的程式码 2 -摸清架构便可轻松掌握全貌 在本文中我们的重点放在要了解一个系统最好是采取由上至下的方式。先试着捕捉系统架构性的观念不要过早钻进细节因为那通常对于你了解全貌没有多大的帮助。阅读程式码不需要从第一行读起我们的目的并不是在于读遍每一段程式码。 基于许多原因程式人需要阅读其他人所写成的程式码。而对程式设计2.0时代的程式人来说最正面的价值在于能读懂别人程式的人才有能力从中萃取自己所需的程式借以提高生产力。 阅读程式码的目的在于了解全貌而非细节 想要读懂别人程式码的根本基础便是了解对方所用的程式语言及命名惯例。有了这个基础之后才算是具备了基本的阅读能力。正如我之前提到的─ ─想要读懂法文写成的小说总不能连法文都不懂吧。阅读程式码和阅读文学作品都需要了解撰写所用的语言及作者习用的语汇。 但我们在阅读文学作品通常是采循序的方式也就是从第一页开始一行一行地读下去依循作者为你铺陈的步调逐渐进到他为你准备好的世界里。阅读程式码却大大不同。我们很少从第一行开始读起因为除非它是很简单的单执行绪程式否则很少这么做。因为要是这么做就很难了解整个系统的全貌。是的我们这边提到了一个重点阅读程式码的目的在于了解系统的全貌而不是在于只是为了地毯式的读遍每一段程式码。 就拿物件导向程式语言所写成的系统来说整个系统被拆解分析成为一个个独立的类别。阅读个别类别的程式码或许可以明白每项类别物件个别的行为。但对于各类别物件之间如何交互影响如何协同工作又很容易陷入盲人摸象的困境。这是因为各类别的程式码只描述个别物件的行为而片段的阅读就只能造就片面的认识。 由上而下厘清架构后便可轻易理解组成关系 如果你想要跳脱困境不想浪费大量时间阅读程式码却始终只能捕捉到对系统片段认识就必须转换到另一种观点来看待系统。从个别的类别行为着手是由下至上自下而上的方法;在阅读程式码时却应该先采由上至下自上而下的方式。对程式码的阅读来说由上至下意谓着你得先了解整个系统架构。 系统的架构是整个系统的骨干支柱。它表现出系统最突出的特征。知道系统架构究竟属于那一种类型通常大大有益于了解系统的个别组成之间的静态及动态关系。有些系统因为所用的技术或框架的关系决定了最上层的架构。例如采用的Java Servlet的/ JSP的技术的应用系统最外层的架构便是以J2EE的或起码的J2EE中的Web容器为根本。 使用的Java Servlet的/ JSP的技术时决定了某些组成之间的关系。例如 Web容器依据web.xml中的内容载入所有的Servlets 听众以及过滤器。每当语境发生事件例如初始化时它便会通知监听类别。每当它收到来自客户端的请求时便会依循设定的所有过滤器链让每个过滤器都有机会检查并处理此一请求最后再将请求导至用来处理该请求的Servlet的。 当我们明白某个系统采用这样的架构时便可以很容易地知道各个组成之间的关系。即使我们还不知道究竟有多少Servlets 但我们会知道每当收到一个请求时总是会有个相对应的服务器来处理它。当想要关注某个请求如何处理时我应该去找出这个请求对应的服务器。 了解架构必须要加上层次感 同样的以爪哇写成的网页应用程式中也许会应用诸如Struts的之类的的MVC框架以及像Hibernate的这样的资料存取框架。它们都可以视为最主要的架构下的较次级架构。而各个应用系统甚至有可能在Struts的及休眠之下建立自有的更次级的架构。 也就是说当我们谈到“架构”这样的观念时必须要有层次感。而不论是那一层级的架构都会定义出各自的角色以及角色间的关系。对阅读者来说相较于直接切入最细微的单一角色行为不如了解某个特定的架构中究竟存在多少角色以及这些角色之间的互动模式比较能够帮助我们了解整个系统的运作方式。 这是一个很重要的关键当你试着进到最细节处之前应该先试着找出参与的角色及他们之间的关系。例如对事件驱动式的架构而言有3个很重要的角色。一个是事件处理的分派器事件调度 一个是事件产生者事件发生器 另一个则是事件处理器事件处理程序 。 事件产生器产生事件并送至事件分派器而事件分派器负责找出各事件相对应的事件处理器并且转交该事件并命令事件处理器加以处理。像的图形用户界面的Windows应用程式便是采用事件驱动式的架构。 当你知道此类的应用程式皆为事件驱动式的架构时你便可以进一步得知在这样的架构下会有3种主要的角色。虽然也许还不清楚整个系统中究竟会需要处理多少事件的类型但对你而言已经建立了对系统全貌最概观的认识。 虽然你还不清楚所有的细节但诸如确切会有那些事件类型之类的资讯在此刻还不重要─ ─不要忘了我们采取的是由上而下的方式要先摸清楚主建筑结构至于壁纸的花色怎么处理那是到了尾声时才会做的事。 探索架构的第一件事找出系统如何初始化 有经验的程式人对于时常被运用的架构都很熟悉。常常只需要瞧上几眼就能明白一个系统所用的架构自然就能够直接联想到其中会存在的角色以及角色间的关系。然而并不是每个系统所用的架构都是大众所熟悉或是一眼能够望穿的。这时候你需要探索。目标同样要放在界定其中的角色以及角色间的静态动态关系。 不论某个系统所采用的架构是否为大部分人所熟知的在试着探索一个系统的长相时我们应该找出来几个答案了解在它所用的架构下下列这件事是如何被完成的一系统如何初始化二与这个系统相接的其他系统或使用者有那些而相接的介面又是什么;三系统如何反应各种事件四系统如何处理各种异常及错误。 系统如何初始化是很重要的一件事因为初始化是为了接下来的所有事物而做的准备。从初始化的方式内容能知道系统做了什么准备对于系统会有什么行为展现也就能得窥一二了。之所以要了解与系统相接的其他系统或使用者 为的是要界定出系统的边界。其他的系统可能会提供输入给我们所探索的系统也可能接收来自这系统的输出了解这边界所在才能确定系统的外观。 而系统所反应的事件类型以及如何反应基本上就代表着系统本身的主要行为模式。最后我们必须了解系统处理异常及错误的方式这同样也是系统的重要行为但容易被忽略。之前我们提到必须先具备一个系统的语言基础才能够进一步加以阅读而在本文中我们的重点放在要了解一个系统最好是采取由上至下的方式。先试着捕捉系统架构性的观念不要过早钻进细节因为那通常对于你了解全貌没有多大的帮助。 阅读他人的程式码 3 -优质工具在手读懂程式非难事 系统的复杂度往往超过人脑的负荷。阅读程式码的时候你会需要更多工具提供协助。使用好的整合式开发环境 IDE 的或文字编辑器就能提供最基本的帮助。 阅读程式码的动作可以是很原始的利用最简单的文字编辑器逐一开启原始码然后凭借着一己的组织能力在不同的程式码间跳跃拼凑出脑中想要构建的图像。 不过系统的复杂度往往超过人脑的负荷。阅读程式码的时候你会需要更多工具提供协助。使用好的整合式开发环境 IDE 的或文字编辑器就能提供最基本的帮助。 善用文字编辑器或IDE中加速解读程式码 许多文字编辑器提供了常见程式语言的语法及关键字标示功能。这对于阅读来说绝对能够起很大的作用。有些文字编辑器例如我常用的编辑器及偶而使用的记事本 甚至能够自动列出某个原始档中所有定义的函式清单更允许你直接从清单中选择函式直接跳跃到该函式的定义位置。这对于阅读程式码的人来说就提供了极佳的便利性。 因为在阅读程式码时最常做的事就是随着程式中的某个控制流将阅读的重心从某个函式移至它所呼叫的另一个函式。所以对程式人来说阅读程式码时最常做的事之一就是找出某个函式位在那一个原始档里接着找到该函式所在的位置。 好的的IDE能够提供的协助就更多了。有些能够自动呈现一些额外的资讯最有用的莫过于函式的原型宣告了。例如有些的IDE支援当游标停留在某函式名称上一段时间后它会以提示的方式显示该函式的原型宣告。 对阅读程式码的人来说在看到程式码中呼叫到某个函式时可以直接利用这样的支援立即取得和这个函式有关的原型资讯马上就能知道呼叫该函式所传入的各个引数的意义而不必等到将该函式的定义位置找出后才能明白这件事。 grep按读者推荐来源透视是一个基本而极为有用的工具 除了选用好的文字编辑器或的IDE之外还有一个基本但却极为有用的工具它就是grep按。熟悉的Unix作业系统的程式人对grep按这个公用程式多半都不陌生。 grep按最大的用途在于它允许我们搜寻某个目录包括递回进入所有子目录中所有指定档案是否有符合指定条件常数字串或正规表示式档案。 倘若有的话则能帮你指出所在的位置。这在阅读程式码时的作用极大。当我们随着阅读的脚步遇上了任何一个不认识但自认为重要的类别函式资料结构定义或变数我们就得找出它究竟位在这茫茫程式码海中的何处才能将这个图块从未知变为已知。 grep按之所以好用就是在于当我们发现某个未知的事物时可以轻易地利用它找出这个未知的事物究竟位在何方。此外虽说grep按是Unix系统的标准公用程式之一但是像视窗这样子的平台也有各种类型的grep按程式。对于在视窗环境工作的程式人来说可以自行选用觉得称手的工具。 gtags可建立索引让搜寻更有效率 grep按虽然好用但是仍然有一些不足之处。第一个缺点在于它并不会为所搜寻的原始码档案索引。每当你搜寻时它都会逐一地找出所有的档案并且读取其中的所有内容过滤出满足指定条件的档案。当专案的原始码数量太大时就会产生搜寻效率不高的问题。 第二个缺点是它只是一个单纯的文字档搜寻工具本身并不会剖析原始码所对应的语言语法。当我们只想针对“函式”名称进行搜寻时它有可能将注解中含有该名称的原始码也一并找了出来。 针对grep按的缺点打算阅读他人程式码的程式人可以考虑使用像是gtags这样子的工具。 gtags是源代码的GNU全局标记系统它不只搜寻文字层次而且因为具备了各种语言的语法剖析器所以在搜寻时可以只针对和语言有关的元素例如类别名称函式名称等。 而且它能针对原始码的内容进行索引这意谓一旦建好索引之后每次搜寻的动作都毋需重新读取所有原始码的内容并逐一搜寻。只需要以现成的索引结构为基础即可有效率的寻找关键段落。 gtags提供了基于命令列的程式让你指定原始码所在的目录执行建立索引的动作。它同时也提供程式让你得如同操作grep按一般针对索引结构进行搜寻及检索。它提供了许多有用的检索方式例如找出专案中定义某个资料结构的档案及定义所在的行号或者是找出专案中所有引用某资料结构的档案以及引用处的行号。 这么一来你就可以轻易地针对阅读程式码时的需求予以检索。相较于grep按所能提供的支援 gtags这样的工具简直是强大许多。 再搭配htags制作的HTML文件更是如虎添翼 还有一个绝对需要一提的工具。这个叫做htags的工具能够帮你将已制作完成的索引结构制作成为一组相互参考的的HTML文件。基本上利用这样的的HTML文件阅读程式码比起单纯地直接阅读原始码来得更有结构。原因是阅读程式码时这样的的HTML文件已经为你建立起在各个原始码档案片段间跳跃的链结。例如图一是针对一个有名的开放原始码专案ffmpeg 由gtags所产生出来的的HTML文件首页的一部分。 htags工具首先为你找出所有定义的Main 函式的档案并且列出所在的函式。找出的Main 函式时常是阅读程式码的第一步因为主要 函式是程式的主要入口点所有的动作皆由此启动它是一切事物的源头。 凭借htags制作的的HTML文件你可以轻易地点击超连结直接进到的Main 函式所在的程式码片段如图二。 当我们检视上述原始码时发现av_register_all 是个陌生无法了解的事物而想要搞懂它究竟是什么可以再继续点击这个函式如图三。这真是太方便了阅读至此你会猛然发现 gtags仿佛就是为了阅读程式码而专门量身打造的利器。 阅读他人的程式码 4 -望文生义进而推敲组件的作用 先建立系统的架构性认识然后透过名称及命名惯例就可以推测出各组件的作用。例如当AOL的Winamp尝试着初始化一个插件时它会呼叫这个结构中的初始化函式以便让每个插件程式有机会初始化自己。当AOL的Winamp打算结束自己或结束某个插件的执行时便会呼叫退出函式。 在阅读程式码的细节之前我们应先试着捕捉系统的运作情境。在采取由上至下的方式时系统性的架构是最顶端的层次而系统的运作情境则是在它之下的另一个层次。 好的说明文件难求拼凑故事的能力很重要 有些系统提供良善的说明文件也许还利用UML的充分描述系统的运作情境。那么对于阅读者来说从系统的分析及设计文件着手便是快速了解系统运作情境的一个途径。 但是并不是每个软体专案都伴随着良好的系统文件而许多极具价值的开放原始码专案也时常不具备此类的文件。对此阅读者必须尝试自行捕捉并适度地记录捕捉到的运作情境。 我喜欢将系统的运作情境比拟成系统会上演的故事情节。在阅读细节性质的程式码前先知道系统究竟会发生那些故事是必备的基本功课。你可以利用熟悉或者自己发明的表示工具描述你所找到的情境。甚至可以只利用简单的列表直接将它们列出。只要能够达到记录的目的对程式码阅读来说都能够提供帮助。或者你也可以利用基于UML中的类别图合作图循序图之类的表示方法做出更详细的描述。 当你能够列出系统可能会有的情境表示你对系统所具备的功能以及在各种情况下的反应都具备概括性的认识。以此为基础便可在任何需要的时候钻进细节处深入了解。 探索架构的第一步─ ─找到程式的入口 在之前我们在一个开发专案中曾经需要将系统所得到的的MP3音讯档放至iPod的这个极受欢迎的播放设备中。 虽然iPod的本身也可以做为可移动式的储存设备但并不是单纯地将MP3播放档案放到中的iPod 就可以让苹果的播放器认得这个档案甚至能够加以播放。 这是因为苹果利用一个特殊的档案结构 iTunes的数据库 记录播放器中可供播放的乐曲播放清单以及乐曲资讯例如专辑名称乐曲长度演唱者等 。为了了解并且试着重复使用既有的程式码我们找到了一个AOL的Winamp的iPod的外挂程式插件 。 AOL的Winamp是个人电脑上极受欢迎的播放软体而我们找到的外挂程式能让的软件直接显示连接至电脑的的iPod中的歌曲资讯并且允许的软件直接播放。 我们追踪与阅读这个外挂程式的思路及步骤如下首先我们要先了解外挂程式的系统架构。很明显的大概浏览过原始码后我们注意到它依循着AOL的Winamp为插件程式所制定的规范也就是说它是实作成的Windows上的DLL的并且透过一个叫做winampGetMediaLibraryPlugin的DLL的函式提供一个名为winampMediaLibraryPlugin的结构。 当我们不清楚系统的架构究竟为何时我们会试着探索而第一步便是找到程式的入口。如何找到呢这会依程式的性质不同而有所差别。 对一个本身就是可独立执行的程式来说我们会找启动程式的主要函式例如对的C / C 来说就是主要 而对爪哇来说便是静无效的main 。在找到入口后再逐一追踪摸索出系统的架构。 但有时我们所欲阅读的程式码是类别库或函式库它只是用来提供多个类别或函式供用户端程式客户程序使用本身并不具单一入口此类的程式码具有多重的入口─ ─每个允许用户端程式呼叫的函式或类别都是它可能的入口。 例如对AOL的Winamp的iPod的插件来说它是一个动态链接库形式的函式库所以当我们想了解它的架构时必须要先找出它对外提供的函式而对的Windows的DLL来说对外提供的函式皆会以dllexport这个关键字来修饰。所以不论是利用grep按或gtags之类的工具我们可以很快从原始码中找到它只有一个DLL的函式这对我们而言真是一个好消息 而这个函式便是上述的winampGetMediaLibraryPlugin 。 系统多会采用相同的架构处理插件程式 如果经验不够的话也许无法直接猜出这个函式的作用。 不过如果你是个有经验的程式人多半能从函式所回传的结构猜出这个函式实际的用途。而事实上当你已经知道它是一个插件程式时就应该要明白它可能采用的就是许多系统都采用的相同架构处理插件程式。 当一个系统采用所谓插件形式的架构时它通常不会知道它的插件究竟会怎么实作实作什么功能。它只会规范插件程式需要满足某个特定介面。当系统初始化时所有的插件都可以依循相同的方式向系统注册合法宣示自己的存在。 虽然系统并不确切知道插件会有什么行为展现但是因为它制定了一个标准的介面所以系统仍然可以预期每个插件能够处理的动作类型。这些动作具体上怎么执行对系统来说并不重要。这也正是物件导向程式设计中的“多型”观念。 随着实务经验归纳常见的架构模式 我想表达的重点是当你“涉世越深”之后所接触的架构越多就越能触类旁通。只需要瞧上几眼就能明白系统所用的架构自然就能够直接联想到其中可能存在的角色以及角色间的关系。 像上述的插件程式手法时常可以在许多允许“外挂”程式码的系统中看到。所以有经验的阅读者多半能够立即反应知道像这样的系统的软件应该是让每个插件程式都写成DLL的函式库。 而每个插件的DLL的函式库中都必须提供winampGetMediaLibraryPlugin 这个函式如果你熟悉的Windows的程式设计你会知道这是利用加载 和GetProcAddress 来达成的一种多型手法 。如果你熟悉设计模式你更会知道这是简单工厂方法这个设计模式的运用。 winampGetMediaLibraryPlugin 所回传的winampMediaLibraryPlugin结构正好就描述了每个AOL的Winamp插件的实作内容。 善用名称可加速了解 利用gtags这个工具我们立即发现这个插件它所定义的初始化退出 PluginMessageProc这三个名称都是函式名称。这暗示在多型的作用下它们都是在某些时间点会由AOL的Winamp核心本体呼叫的函式。 名称及命名惯例是很重要的。看到“初始化” 我们会知道它的作用多半是进行初始化的动作而“退出”大概就是结束时处理函式而PluginMessageProc多半就是各种讯息的处理常式过程通常是程序的简写所以PluginMessageProc意指插件讯息程序了。 “望文生义”很重要我们看到函式的名称就可以猜想到它所代表的作用例如当AOL的Winamp尝试着初始化一个插件时它会呼叫这个结构中的初始化函式以便让每个插件程式有机会初始化自己;当AOL的Winamp打算结束自己或结束某个插件的执行时便会呼叫退出函式。当AOL的Winamp要和插件程式沟通时它会发送各种不同的讯息至插件而插件程式必须对此做出回应。 我们甚至不需要检视这几个函式的内容就可以做出推测而这样的假设事实上也是正确的。 阅读他人的程式码 5 -找到程式入口再由上而下抽丝剥茧 根据需要决定展开的层数或展开特定节点并记录树状结构然后适度忽略不需要了解的细节─这是一个很重要的态度。因为你不会一次就需要所有的细节阅读都是有目的的每次的阅读也许都在探索程式中不同的区域。 探索系统架构的第一步就是找到程式的入口点。找到入口点后多半采取由上而下自上而下的方式由最外层的结构一层一层逐渐探索越来越多的细节。 我们的开发团队曾针对AOL的Winamp的iPod的插件进行阅读及探索不仅找到入口点也找出并理解它最根本的基础架构。从这个入口点可以往下再展开一层分别找到三个重要的组成及其意义 ●的init 初始化动作 ●退出 终止化动作 ● PluginMessageProc 以讯息的方式处理程式所必须处理的各种事件 展开的同时随手记录树状结构 当我们从一个入口点找到三个分支后可以顺着每个分支再展开一层所以分别继续阅读的init 退出以及PluginMessageProc的内容并试着再展开一层。阅读的同时你可以在文件中试着记录展开的树状结构。 ●的init 初始化动作 ● itunesdb_init_cc 建立存取iTunes的数据库的同步物件 ●初始化资料结构 ●初始化的GUI元素 ●载入设定 ●建立日志档 ● autoDetectIpod 侦测的iPod插入的执行绪 ●退出 终止化动作 ● itunesdb_del_cc 终止存取iTunes的数据库的同步物件 ●关闭日志档 ●终止化图形用户界面元素 ● PluginMessageProc 以讯息的方式处理程式所必须面临的各种事件 ●执行所连接之苹果的MessageProc 这部分必须要留意几个重点。首先应该一边阅读一边记录文件。因为人的记忆力通常有限对于陌生的事物更是容易遗忘因此边阅读边记录是很好的辅助。 再者因为我们采取由上而下的方式从一个点再分支出去成为多个点因此通常也会以树状的方式记录。除此之外每次只试着往下探索一层。从的init 来看你便会明白。 以下试着摘要的init 的内容 诠释的init itunesdb_init_cc ; currentiPod 空; 苹果新C_ItemList ; …略 conf_file 字符* SendMessage plugin.hwndWinampParent WM_WA_IPC 0 IPC_GETINIFILE ; m_treeview GetDlgItem plugin.hwnd LibraryParent 0×3fd ; / /这个数字实际上是魔术 …略 g_detectAll GetPrivateProfileInt “ ml_ipod ” “ detectAll ” 0 conf_file 0 ; …略 g_log GetPrivateProfileInt “ ml_ipod ” “日志” 0 conf_file 0 ; …略 g_logfile 打开 g_logfilepath 有“ A ” ; …略 autoDetectIpod ; 返回0 ; 因为我们只试着多探索一层而目的是希望发掘出下一层的子动作。所以在的init 中看到像“ itunesdb_init_cc ; ”这样的函式呼叫动作时我们知道它是在初始化 之下的一个独立子动作所以可以直接将它列入。但是当看到如下的程式行 currentiPod 空; 苹果新C_ItemList ; 我们并不会将它视为的init 下的一个独立的子动作。因为好几行程式才构成一个具有独立抽象意义的子动作。例如以上这两行构成了一个独立的抽象意义也就是初始化所需的资料结构。 理论上原来的程式撰写者有可能撰写一个叫做init_data_structure 的函式包含这两行程式码。这样做可读性更高然而基于种种理由原作者并没有这么做。身为阅读者必须自行解读将这几行合并成单一个子动作并赋予它一个独立的意义─ ─初始化资料结构。 无法望文生义的函式先试着预看一层 对于某些不明作用的函式叫用不是望其文便能生其义的。当我们看到“ itunesdb_init_cc ”这个名称时我们或许能从“ itunesdb_init ”的字眼意识到这个函式和苹果所采用的的iTunes数据库的初始化有关但“循环”却实在令人费解。为了理解这一层某个子动作的真实意义有时免不了要往前多看一层。 原来它是用来初始化同步化机制用的物件。作用在于这程式一定是用了某个内部的资料结构来储存的iTunes数据库而这资料结构有可能被多执行绪存取所以必须以同步物件此处是视窗的临界区加以保护。 所以说当我们试着以树状的方式逐一展开每个动作的子动作时有时必须多看一层才能真正了解子动作的意义。因为有了这样的动作我们可以在展开树状结构中为itunesdb_init_cc 附上补充说明建立存取iTunes的数据库的同步物件。这么一来当我们在检视自己所写下的树状结构时就能轻易一目了然的理解每个子动作的真正作用。 根据需要了解的粒度决定展开的层数 我们究竟需要展开多少层呢这个问题和阅读程式码时所需的“粒度粒度 ”有关。如果我们只是需要概括性的了解那么也许展开两层或三层就能够对程式有基础的认识。倘若需要更深入的了解就会需要展开更多的层次才行。 有时候你并不是一视同仁地针对每个动作都展开到相同深度的层次。也许你会基于特殊的需求专门针对特定的动作展开至深层。例如我们阅读AOL的Winamp的iPod插件的程式目录其实是想从中了解究竟应该如何存取的iPod上的iTunes的数据库使我们能够将MP3播放歌曲或播放清单加至此数据库中并于的iPod中播放。 当我们层层探索与分解之后找到了parseIpodDb 从函式名称判断它是我们想要的。因为它代表的正是剖析iPod的数据库正是我们此次阅读的重点也就达成阅读这程式码的目的。 我们强调一种不同的做法在阅读程式码时多半采取由上而下的方式而本文建议了一种记录阅读的方式就是试着记录探索追踪时层层展开的树状结构。你可以视自己需要了解的深入程度再决定要展开的层数。你更可以依据特殊的需要只展开某个特定的节点以探索特定的细目。 适度地忽略不需要了解的细节是一个很重要的态度因为你不会一次就需要所有的细节阅读都是有目的的。每次的阅读也许都在探索程式中不同的区域;而每次探索时你都可以增补树状结构中的某个子结构。渐渐地你就会对这个程式更加的了解。 阅读他人的程式码 6 -阅读的乐趣透过程式码认识作者 即便每个人的写作模式多半受到他人的影响程式人通常还是会融合多种风格而成为自己独有的特色如果你知道作者程式设计的偏好阅读他的程式码就更得心应手。 阅读程式码时多半会采取由上而下抽丝剥茧的方式。透过记录层层展开的树状结构程式人可以逐步地建立起对系统的架构观而且可以依照需要的粒度粒度 决定展开的层次及精致程度。 建立架构观点的认识是最重要的事情。虽然这一系列的文章前提为“阅读他人的程式码” 但我们真正想做的工作并不在于彻底地详读每一行程式码的细节而是想要透过重点式的程式码“摘读” 达到对系统所需程度的了解。每个人在阅读程式码的动机不尽相同需要了解的程度也就有深浅的分别。只有极为少数的情况下你才会需要细读每一行程式码。 阅读程式码是新时代程式人必备的重要技能 这一系列的文章至此已近尾声回顾曾探讨的主题我们首先研究了阅读程式码的动机。尤其在开放原始码的风气如此之盛的情况下妥善利用开放原始码所提供的资源不仅能够更快学习到新的技术同时在原始码版权合适时还可以直接利用现成的程式码大幅地提高开发阶段的生产力。所以阅读程式码俨然成为了新时代程式人必备的重要技能之一。 接着我们提到了阅读程式码前的必要准备包括了对程式语言命名惯例的了解等等。在此之后我们反覆提起了“由上而下”的阅读方向的重要性。 由上而下的阅读方式是因为我们重视架构更胜于细节。从最外层的架构逐一向内探索每往内探索一层我们了解系统的粒度就增加了一个等级。当你识别出系统所用的架构时便能够轻易了解在这个架构下会有的角色以及它们之间的动态及静态的关系。如此一来许多资讯便不言可喻毋需额外花费力气便能够快速理解。 好的名称能够摘要性地点出实体的作用 追踪原始码时固然可以用本来的方式利用编辑器开启所需的档案然后利用编辑器提供的机制阅读但是倘若能够善用工具阅读程式码的效率及品质都能大大提升。在本系列文章中我们介绍了一些工具或许你还可以在坊间找到其他更有用的工具。 我在这一系列的文章中实际带着大家阅读追踪了一个名为ml_pod的开放原始码专案。它是一个AOL的Winamp的iPod的外挂程式。在追踪的过程中我们试着印证这一系列文中所提到的观念及方法。我们采用逐渐开展的树状结构来记录追踪的过程并借以建立起对系统的概观认识。 就原始码的阅读来说之前的讨论涉及了工具面及技巧面。但还有一些主题不在这两个范畴之内例如善用名称赋予你的提示。名称做为隐喻隐喻的作用很大好的名称能够摘要性地点出实体的作用例如我们看到autoDetectIpod 自然而然能够想像它的作用在于自动自动侦测检测的iPod的存在。 我们在展开树状结构时有时候需要预看一层有时却不需要这么做便可得到印证。程式人都会有惯用的名称以及组合名称的方法倘若能够从名称上理解便毋需钻进细节可以省去相当多的时间。例如当我们看到parseIpodDb 时便可以轻易了解它是剖析解析的iPod的资料库 DB 的因此便不需要立即钻进parseIpodDb 中查看底细。 尽管如此能否理解程式人命名的用意和自身的经验以及是否了解原作者的文化背景是息息相关的。 命名本身就是一种文化产物。不同的程式人文化就会衍生出不同的命名文化。当你自己的经验丰富看过及接触过的程式码也多时对于名称的感受及联想的能力自然会有不同。 这种感受和联想的能力究竟应该如何精进很难具体描述。就我个人的经验多观察不同命名体系的差异并且尝试归纳彼此之间的异同有助于更快地提升对名称的感受及联想力。 转换立场理解作者的思考方式 除了工具及技巧之外 “想要阅读程式码得先试着阅读写这个程式码的程式人的心。 ”这句话说来十分抽象或许也令人难以理解。 当你在阅读一段程式码时或许可以试着转换自己的立场从旁观者的角度转换成为写作者的心态揣摩原作者的心理及处境。当你试着设身处地站在他的立场透过他的思考方式来阅读追踪他所写下的程式码将会感觉更加流畅。 许多软体专案都不是由单一程式人所独力完成。因此在这样的专案中便有可能呈现多种不同的风格。 许多专案会由架构师决定主体的架构及运作有既定实施的命名惯例及程式设计需要遵守方针。在多人开发的模式下越是好的软体专案越看不出某程式码片段究竟是由谁所写下的。 不过有些开放原始码的专案往往又整合了其他开放原始码的专案。有的时候也很难求风格的统一便会出现混杂的情况。好比之前提到的ml_pod专案因为程式码中混合了不同的来源而呈现风格不一致的情况。 我在阅读非自己所写的程式码时会观察原作者写作的习惯借以对应到脑中所记忆的多种写作模型。在阅读的过程中读完几行程式码我会试着猜想原作者在写下这段程式码时的心境。他写下这段程式码的用意是什么为什么他会采取这样的写法顺着原作者的思考理路阅读自己的思考才能更贴近对方写作当时的想法。 当你短暂化身为原作者时才能更轻易的理解他所写下的程式码。 如果你能知道原作者的背景程式设计时的偏好阅读他的程式码就更能得心应手了。 从程式码着手认识作者独有的风格进而见贤思齐 我在阅读别人写下的程式码时我会试着猜想原作者究竟是属于那一种“流派”呢每个人都有自己独特的写作模式即便每个人的写作模式多半受到他人的影响─ ─不论是书籍的作者学习过程中的指导者或一同参与专案的同侪但每个程式人通常会融合多种风格而成为自己独有的风格。 物件导向的基本教义派总是会以他心中觉得最优雅的物件导向方式来撰写程式。而阅读惯用善用设计模式的程式人所写下的程式码时不难推想出他会在各种常见的应用情境下套用哪些模式。 有些时候在阅读之初你并不知道原作者的习性跟喜好甚至你也不知道他的功力。但是在阅读之后你会慢慢地从一个程式人所写下的程式码开始认识他。 你或许会在阅读他人的程式码时发现令人拍案叫绝的技巧或设计。你也有可能在阅读的同时发现原作者所留下的缺失或写作时的缺点而暗自警惕于心。这也算是阅读他人程式码时的一项乐趣。 当你从视阅读他人的程式码为畏途转变成为可以从中获取乐趣的时候我想你又进到了另一个境界。 来源http://www.uncleli.cn/archives/553.html -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- 最近在看一个项目看到那个项目就觉得现在心里有点恐惧了真的不知道从何看起那些字段不知道从哪个角度去猜因为那个模块不是那么的让人满意功能用起来不那么友好所以要重新做一遍很有耐心的去尝试看些代码顺便找些看代码的方法找到了这篇可以借鉴的文章通篇看下来对其他的项目应该都可行的对别人也应该可行的 但是对于要重整的项目我确实没有办法了没有那么多时间去看了虽说起点不低了整个看下来老大不急才怪呢 多走几遍流程重头再来 如何看懂别人的源码 我们在写程式时有不少时间都是在看别人的代码。 例如看小组的代码看小组整合的守则若一开始没规划怎么看 就会“噜看噜苦台语 ” 不管是参考也好从开源抓下来研究也好为了了解箇中含意在有限的时间下不免会对庞大的源代码解读感到压力。 网路上有一篇关于分析看代码的方法做为程式设计师的您不妨参考看看 换个角度来分析。 也能更有效率的解读你想要的程式码片段。 六个章节 1 读懂程式码使心法皆为我所用。 2 摸清架构便可轻松掌握全貌。 3 优质工具在手读懂程式非难事。 4 望文生义进而推敲组件的作用。 5 找到程式入口再由上而下抽丝剥茧。 6 阅读的乐趣透过程式码认识作者。 阅读他人的程式码 1 ---读懂程式码使心法皆为我所用 程式码是别人写的只有原作者才真的了解程式码的用途及涵义。许多程式人心里都有一种不自觉的恐惧感深怕被迫去碰触其他人所写的程式码。但是与其抗拒接收别人的程式码不如彻底了解相关的语言和惯例当成是培养自我实力的基石。 对大多数的程式人来说撰写程式码或许是令人开心的一件事情但我相信有更多人视阅读他人所写成的程式码为畏途。许多人宁可自己重新写过一遍程式码也不愿意接收别人的程式码进而修正错误维护它们甚至加强功能。 这其中的关键究竟在何处呢若是一语道破其实也很简单程式码是别人写的只有原作者才真的了解程式码的用途及涵义。许多程式人心里都有一种不自觉的恐惧感深怕被迫去碰触其他人所写的程式码。这是来自于人类内心深处对于陌生事物的原始恐惧。 读懂别人写的程式码让你收获满满 不过基于许多现实的原因程式人时常受迫要去接收别人的程式码。例如同事离职了必须接手他遗留下来的工作也有可能你是刚进部门的菜鸟而同事经验值够了升级了风水轮流转一代菜鸟换菜鸟。甚至你的公司所承接的专案必须接手或是整合客户前一个厂商所遗留下来的系统你们手上只有那套系统的原始码运气好时还有数量不等的文件 。 诸如此类的故事其实时常在程式人身边或身上持续上演着。许多程式人都将接手他人的程式码当做一件悲惨的事情。每个人都不想接手别人所撰写的程式码因为不想花时间去探索宁可将生产力花在产生新的程式码而不是耗费在了解这些程式码上。 很遗憾的是上述的情况对程式人来说很难避免。我们总是必须碰触到其他人所写成的程式码甚至必须了解它加以修改。对于这项需求在现今开放原始码的风气如此盛行的今日正如之前的“程式设计2.0 ”文中所提到的你可以透过开放原始码学习到新的技术学习到高手的架构设计大幅提高学习的效率及效果。你甚至可以直接自开放原始码专案中抽取提炼出自己所需的程式码站在巨人的肩膀上直接由彼端获得所需的生产力。从这个观点来看读懂别人所写的程式码就不再只是从负面观点的“被迫接收” 而是极具正面价值的“汲取养份。 ” 先了解系统架构与行为模式再细读 倘若撰写程式码是程式人的重要技艺之一那么读懂别人的程式码接着加以修改也势必是另一个重要的技艺。 如果你不能熟悉这项工作不仅在遭逢你所不愿面对的局面时无法解决眼前接手他人程式码的难题更重要的是当你看着眼前现成的程式码却不知如何从中撷取自己所需导致最后只能入宝山空手回望之兴叹。 接触他人的程式码大致上可以分为三种程度一了解二修改扩充三抽取提炼。了解别人的程式码是最基础的工作倘若不能了解自己要处理的程式码就甭论修改或扩充更不可能去芜存菁从中萃取出自己所需回收再利用别人所撰写的程式码。虽说是“阅读” 但程式码并不像文章或小说一样透过这种做法便能够获得一定程度的了解。阅读文章或小说时几乎都是循序地阅读你只消翻开第一页一行行阅读下去即可。但是有许多程式人在试着阅读其他人的程式码时却往往有不知如何读起的困难。 或许找到系统的第一页也就是程式码执行的启始点并不难但是复杂度高的系统有时十分庞大有时千头万绪。 从程式码的启始点开始读起一来要循序读完所有的程式码旷日费时二来透过这种方式来了解系统很难在脑中构建出系统的面貌进而了解到系统真正的行为。所以阅读程式码的重点不在于读完每一行程式码而是在于有效率地透过探索及阅读从而了解系统的架构及行为模式。以便在你需要了解任何片段的细节实作时能够很快在脑上对映到具体的程式码位置直到那一刻才是细读的时机。 熟悉沟通语言与惯例用语 不论如何有些基本的准备是阅读他人程式码时必须要有的。 首先你最好得了解程式码写成的程式语言。想要读懂法文写成的小说总不能连法文都不懂吧。有些情况则很特殊。我们虽然不懂该程式码撰写所用的语言但是因为现代语言的高阶化而且流行的程式语言多半都是血统相近所以即使不那么熟悉有时也可勉力为之。 除了认识所用语言之外再来就是要先确认程式码所用的命名惯例命名惯例 。了解命名惯例很重要不同的程式人或开发团队差异可能很大。 这命名惯例涵盖的范围通常包括了变数的名称函式的名称类别如果是物件导向的话的名称原始码档案甚至是专案建构目录的名称。倘若使用了像设计模式之类的方法这些名称更有一些具体的表述方式。 命名惯例有点像是程式人在程式语言之上另行建构的一组沟通行话。程式人会透过共通约束遵守的命名惯例来表达一些较高阶的概念。例如有名的匈牙利式命名法便将变数名称以属性型别说明合并在一起描述。对程式人来说这种方式能够提供更丰富的资讯以了解该变数的作用及性质。 对程式码阅读来说熟悉这个做法之所以重要是因为当你了解整个系统所采用的惯例时你便能试着以他们所共同操用的语汇来进行理解。倘若不能了解其所用的惯例那么这些额外提供的资讯就无法为你所用。像以设计模式写成的程式码同样处处充满着模式的名称诸如工厂门面代理等等。以这些名称指涉的类别也直接透过名称表达了它们自身的作用。对于懂得这命名惯例的读者来说不需要深入探索也能很快捕捉到这些类别的意义。 当你拿到一套必须阅读的程式码时最好先取得命名惯例的说明文件。然而并不是每套程式码都附有此类的说明文件。另一个方式就是自己到程式码中大略浏览一遍有经验的程式人可以轻易发掘出该系统所用的命名惯例。 常见的命名方式不脱那几类这时候经验就很重要倘若你知道的惯例越多就越能轻易识别他人所用的惯例。如果运气很糟程式码所用的惯例是前所未见的那么你也得花点时间归纳凭自己的力量找出这程式码命名上的规则。 掌握程式码撰写者的心态与习惯 大多数的程式码基本上都依循一致的命名惯例。不过运气更差的时候一套系统中可能会充斥着多套命名惯例。这有可能是因为开发团队由多组人马所构成每组人马都有不同的文化而在专案开发管理又没有管控得宜所造成。最糟的情况程式码完全没有明显的惯例可言这时候阅读的难度就更高了。 想要阅读程式码得先试着体会程式码作者的“心” 。想要这么做就得多了解对方所使用的语言以及惯常运用的语汇。在下一回中我们将继续探讨阅读程式码的相关议题。 阅读他人的程式码 2 -摸清架构便可轻松掌握全貌 在本文中我们的重点放在要了解一个系统最好是采取由上至下的方式。先试着捕捉系统架构性的观念不要过早钻进细节因为那通常对于你了解全貌没有多大的帮助。阅读程式码不需要从第一行读起我们的目的并不是在于读遍每一段程式码。 基于许多原因程式人需要阅读其他人所写成的程式码。而对程式设计2.0时代的程式人来说最正面的价值在于能读懂别人程式的人才有能力从中萃取自己所需的程式借以提高生产力。 阅读程式码的目的在于了解全貌而非细节 想要读懂别人程式码的根本基础便是了解对方所用的程式语言及命名惯例。有了这个基础之后才算是具备了基本的阅读能力。正如我之前提到的─ ─想要读懂法文写成的小说总不能连法文都不懂吧。阅读程式码和阅读文学作品都需要了解撰写所用的语言及作者习用的语汇。 但我们在阅读文学作品通常是采循序的方式也就是从第一页开始一行一行地读下去依循作者为你铺陈的步调逐渐进到他为你准备好的世界里。阅读程式码却大大不同。我们很少从第一行开始读起因为除非它是很简单的单执行绪程式否则很少这么做。因为要是这么做就很难了解整个系统的全貌。是的我们这边提到了一个重点阅读程式码的目的在于了解系统的全貌而不是在于只是为了地毯式的读遍每一段程式码。 就拿物件导向程式语言所写成的系统来说整个系统被拆解分析成为一个个独立的类别。阅读个别类别的程式码或许可以明白每项类别物件个别的行为。但对于各类别物件之间如何交互影响如何协同工作又很容易陷入盲人摸象的困境。这是因为各类别的程式码只描述个别物件的行为而片段的阅读就只能造就片面的认识。 由上而下理清架构后便可轻易理解组成关系 如果你想要跳脱困境不想浪费大量时间阅读程式码却始终只能捕捉到对系统片段认识就必须转换到另一种观点来看待系统。从个别的类别行为着手是由下至上自下而上的方法;在阅读程式码时却应该先采由上至下自上而下的方式。对程式码的阅读来说由上至下意谓着你得先了解整个系统架构。 系统的架构是整个系统的骨干支柱。它表现出系统最突出的特征。知道系统架构究竟属于那一种类型通常大大有益于了解系统的个别组成之间的静态及动态关系。有些系统因为所用的技术或框架的关系决定了最上层的架构。例如采用的Java Servlet的/ JSP的技术的应用系统最外层的架构便是以J2EE的或起码的J2EE中的Web容器为根本。 使用的Java Servlet的/ JSP的技术时决定了某些组成之间的关系。例如 Web容器依据web.xml中的内容载入所有的Servlets 听众以及过滤器。每当语境发生事件例如初始化时它便会通知监听类别。每当它收到来自客户端的请求时便会依循设定的所有过滤器链让每个过滤器都有机会检查并处理此一请求最后再将请求导至用来处理该请求的Servlet的。 当我们明白某个系统采用这样的架构时便可以很容易地知道各个组成之间的关系。即使我们还不知道究竟有多少Servlets 但我们会知道每当收到一个请求时总是会有个相对应的服务器来处理它。当想要关注某个请求如何处理时我应该去找出这个请求对应的服务器。 了解架构必须要加上层次感 同样的以爪哇写成的网页应用程式中也许会应用诸如Struts的之类的的MVC框架以及像Hibernate的这样的资料存取框架。它们都可以视为最主要的架构下的较次级架构。而各个应用系统甚至有可能在Struts的及休眠之下建立自有的更次级的架构。 也就是说当我们谈到“架构”这样的观念时必须要有层次感。而不论是那一层级的架构都会定义出各自的角色以及角色间的关系。对阅读者来说相较于直接切入最细微的单一角色行为不如了解某个特定的架构中究竟存在多少角色以及这些角色之间的互动模式比较能够帮助我们了解整个系统的运作方式。 这是一个很重要的关键当你试着进到最细节处之前应该先试着找出参与的角色及他们之间的关系。例如对事件驱动式的架构而言有3个很重要的角色。一个是事件处理的分派器事件调度 一个是事件产生者事件发生器 另一个则是事件处理器事件处理程序 。 事件产生器产生事件并送至事件分派器而事件分派器负责找出各事件相对应的事件处理器并且转交该事件并命令事件处理器加以处理。像的图形用户界面的Windows应用程式便是采用事件驱动式的架构。 当你知道此类的应用程式皆为事件驱动式的架构时你便可以进一步得知在这样的架构下会有3种主要的角色。虽然也许还不清楚整个系统中究竟会需要处理多少事件的类型但对你而言已经建立了对系统全貌最概观的认识。 虽然你还不清楚所有的细节但诸如确切会有那些事件类型之类的资讯在此刻还不重要─ ─不要忘了我们采取的是由上而下的方式要先摸清楚主建筑结构至于壁纸的花色怎么处理那是到了尾声时才会做的事。 探索架构的第一件事找出系统如何初始化 有经验的程式人对于时常被运用的架构都很熟悉。常常只需要瞧上几眼就能明白一个系统所用的架构自然就能够直接联想到其中会存在的角色以及角色间的关系。然而并不是每个系统所用的架构都是大众所熟悉或是一眼能够望穿的。这时候你需要探索。目标同样要放在界定其中的角色以及角色间的静态动态关系。 不论某个系统所采用的架构是否为大部分人所熟知的在试着探索一个系统的长相时我们应该找出来几个答案了解在它所用的架构下下列这件事是如何被完成的一系统如何初始化二与这个系统相接的其他系统或使用者有那些而相接的介面又是什么;三系统如何反应各种事件四系统如何处理各种异常及错误。 系统如何初始化是很重要的一件事因为初始化是为了接下来的所有事物而做的准备。从初始化的方式内容能知道系统做了什么准备对于系统会有什么行为展现也就能得窥一二了。之所以要了解与系统相接的其他系统或使用者 为的是要界定出系统的边界。其他的系统可能会提供输入给我们所探索的系统也可能接收来自这系统的输出了解这边界所在才能确定系统的外观。 而系统所反应的事件类型以及如何反应基本上就代表着系统本身的主要行为模式。最后我们必须了解系统处理异常及错误的方式这同样也是系统的重要行为但容易被忽略。之前我们提到必须先具备一个系统的语言基础才能够进一步加以阅读而在本文中我们的重点放在要了解一个系统最好是采取由上至下的方式。先试着捕捉系统架构性的观念不要过早钻进细节因为那通常对于你了解全貌没有多大的帮助。 阅读他人的程式码 3 -优质工具在手读懂程式非难事 系统的复杂度往往超过人脑的负荷。阅读程式码的时候你会需要更多工具提供协助。使用好的整合式开发环境 IDE 的或文字编辑器就能提供最基本的帮助。 阅读程式码的动作可以是很原始的利用最简单的文字编辑器逐一开启原始码然后凭借着一己的组织能力在不同的程式码间跳跃拼凑出脑中想要构建的图像。 不过系统的复杂度往往超过人脑的负荷。阅读程式码的时候你会需要更多工具提供协助。使用好的整合式开发环境 IDE 的或文字编辑器就能提供最基本的帮助。 善用文字编辑器或IDE中加速解读程式码 许多文字编辑器提供了常见程式语言的语法及关键字标示功能。这对于阅读来说绝对能够起很大的作用。有些文字编辑器例如我常用的编辑器及偶而使用的记事本 甚至能够自动列出某个原始档中所有定义的函式清单更允许你直接从清单中选择函式直接跳跃到该函式的定义位置。这对于阅读程式码的人来说就提供了极佳的便利性。 因为在阅读程式码时最常做的事就是随着程式中的某个控制流将阅读的重心从某个函式移至它所呼叫的另一个函式。所以对程式人来说阅读程式码时最常做的事之一就是找出某个函式位在那一个原始档里接着找到该函式所在的位置。 好的的IDE能够提供的协助就更多了。有些能够自动呈现一些额外的资讯最有用的莫过于函式的原型宣告了。例如有些的IDE支援当游标停留在某函式名称上一段时间后它会以提示的方式显示该函式的原型宣告。 对阅读程式码的人来说在看到程式码中呼叫到某个函式时可以直接利用这样的支援立即取得和这个函式有关的原型资讯马上就能知道呼叫该函式所传入的各个引数的意义而不必等到将该函式的定义位置找出后才能明白这件事。 grep按读者推荐来源透视是一个基本而极为有用的工具 除了选用好的文字编辑器或的IDE之外还有一个基本但却极为有用的工具它就是grep按。熟悉的Unix作业系统的程式人对grep按这个公用程式多半都不陌生。 grep按最大的用途在于它允许我们搜寻某个目录包括递回进入所有子目录中所有指定档案是否有符合指定条件常数字串或正规表示式档案。 倘若有的话则能帮你指出所在的位置。这在阅读程式码时的作用极大。当我们随着阅读的脚步遇上了任何一个不认识但自认为重要的类别函式资料结构定义或变数我们就得找出它究竟位在这茫茫程式码海中的何处才能将这个图块从未知变为已知。 grep按之所以好用就是在于当我们发现某个未知的事物时可以轻易地利用它找出这个未知的事物究竟位在何方。此外虽说grep按是Unix系统的标准公用程式之一但是像视窗这样子的平台也有各种类型的grep按程式。对于在视窗环境工作的程式人来说可以自行选用觉得称手的工具。 gtags可建立索引让搜寻更有效率 grep按虽然好用但是仍然有一些不足之处。第一个缺点在于它并不会为所搜寻的原始码档案索引。每当你搜寻时它都会逐一地找出所有的档案并且读取其中的所有内容过滤出满足指定条件的档案。当专案的原始码数量太大时就会产生搜寻效率不高的问题。 第二个缺点是它只是一个单纯的文字档搜寻工具本身并不会剖析原始码所对应的语言语法。当我们只想针对“函式”名称进行搜寻时它有可能将注解中含有该名称的原始码也一并找了出来。 针对grep按的缺点打算阅读他人程式码的程式人可以考虑使用像是gtags这样子的工具。 gtags是源代码的GNU全局标记系统它不只搜寻文字层次而且因为具备了各种语言的语法剖析器所以在搜寻时可以只针对和语言有关的元素例如类别名称函式名称等。 而且它能针对原始码的内容进行索引这意谓一旦建好索引之后每次搜寻的动作都毋需重新读取所有原始码的内容并逐一搜寻。只需要以现成的索引结构为基础即可有效率的寻找关键段落。 gtags提供了基于命令列的程式让你指定原始码所在的目录执行建立索引的动作。它同时也提供程式让你得如同操作grep按一般针对索引结构进行搜寻及检索。它提供了许多有用的检索方式例如找出专案中定义某个资料结构的档案及定义所在的行号或者是找出专案中所有引用某资料结构的档案以及引用处的行号。 这么一来你就可以轻易地针对阅读程式码时的需求予以检索。相较于grep按所能提供的支援 gtags这样的工具简直是强大许多。 再搭配htags制作的HTML文件更是如虎添翼 还有一个绝对需要一提的工具。这个叫做htags的工具能够帮你将已制作完成的索引结构制作成为一组相互参考的的HTML文件。基本上利用这样的的HTML文件阅读程式码比起单纯地直接阅读原始码来得更有结构。原因是阅读程式码时这样的的HTML文件已经为你建立起在各个原始码档案片段间跳跃的链结。例如图一是针对一个有名的开放原始码专案ffmpeg 由gtags所产生出来的的HTML文件首页的一部分。 htags工具首先为你找出所有定义的Main 函式的档案并且列出所在的函式。找出的Main 函式时常是阅读程式码的第一步因为主要 函式是程式的主要入口点所有的动作皆由此启动它是一切事物的源头。 凭借htags制作的的HTML文件你可以轻易地点击超连结直接进到的Main 函式所在的程式码片段如图二。 当我们检视上述原始码时发现av_register_all 是个陌生无法了解的事物而想要搞懂它究竟是什么可以再继续点击这个函式如图三。这真是太方便了阅读至此你会猛然发现 gtags仿佛就是为了阅读程式码而专门量身打造的利器。 阅读他人的程式码 4 -望文生义进而推敲组件的作用 先建立系统的架构性认识然后透过名称及命名惯例就可以推测出各组件的作用。例如当AOL的Winamp尝试着初始化一个插件时它会呼叫这个结构中的初始化函式以便让每个插件程式有机会初始化自己。当AOL的Winamp打算结束自己或结束某个插件的执行时便会呼叫退出函式。 在阅读程式码的细节之前我们应先试着捕捉系统的运作情境。在采取由上至下的方式时系统性的架构是最顶端的层次而系统的运作情境则是在它之下的另一个层次。 好的说明文件难求拼凑故事的能力很重要 有些系统提供良善的说明文件也许还利用UML的充分描述系统的运作情境。那么对于阅读者来说从系统的分析及设计文件着手便是快速了解系统运作情境的一个途径。 但是并不是每个软体专案都伴随着良好的系统文件而许多极具价值的开放原始码专案也时常不具备此类的文件。对此阅读者必须尝试自行捕捉并适度地记录捕捉到的运作情境。 我喜欢将系统的运作情境比拟成系统会上演的故事情节。在阅读细节性质的程式码前先知道系统究竟会发生那些故事是必备的基本功课。你可以利用熟悉或者自己发明的表示工具描述你所找到的情境。甚至可以只利用简单的列表直接将它们列出。只要能够达到记录的目的对程式码阅读来说都能够提供帮助。或者你也可以利用基于UML中的类别图合作图循序图之类的表示方法做出更详细的描述。 当你能够列出系统可能会有的情境表示你对系统所具备的功能以及在各种情况下的反应都具备概括性的认识。以此为基础便可在任何需要的时候钻进细节处深入了解。 探索架构的第一步─ ─找到程式的入口 在之前我们在一个开发专案中曾经需要将系统所得到的的MP3音讯档放至iPod的这个极受欢迎的播放设备中。 虽然iPod的本身也可以做为可移动式的储存设备但并不是单纯地将MP3播放档案放到中的iPod 就可以让苹果的播放器认得这个档案甚至能够加以播放。 这是因为苹果利用一个特殊的档案结构 iTunes的数据库 记录播放器中可供播放的乐曲播放清单以及乐曲资讯例如专辑名称乐曲长度演唱者等 。为了了解并且试着重复使用既有的程式码我们找到了一个AOL的Winamp的iPod的外挂程式插件 。 AOL的Winamp是个人电脑上极受欢迎的播放软体而我们找到的外挂程式能让的软件直接显示连接至电脑的的iPod中的歌曲资讯并且允许的软件直接播放。 我们追踪与阅读这个外挂程式的思路及步骤如下首先我们要先了解外挂程式的系统架构。很明显的大概浏览过原始码后我们注意到它依循着AOL的Winamp为插件程式所制定的规范也就是说它是实作成的Windows上的DLL的并且透过一个叫做winampGetMediaLibraryPlugin的DLL的函式提供一个名为winampMediaLibraryPlugin的结构。 当我们不清楚系统的架构究竟为何时我们会试着探索而第一步便是找到程式的入口。如何找到呢这会依程式的性质不同而有所差别。 对一个本身就是可独立执行的程式来说我们会找启动程式的主要函式例如对的C / C 来说就是主要 而对爪哇来说便是静无效的main 。在找到入口后再逐一追踪摸索出系统的架构。 但有时我们所欲阅读的程式码是类别库或函式库它只是用来提供多个类别或函式供用户端程式客户程序使用本身并不具单一入口此类的程式码具有多重的入口─ ─每个允许用户端程式呼叫的函式或类别都是它可能的入口。 例如对AOL的Winamp的iPod的插件来说它是一个动态链接库形式的函式库所以当我们想了解它的架构时必须要先找出它对外提供的函式而对的Windows的DLL来说对外提供的函式皆会以dllexport这个关键字来修饰。所以不论是利用grep按或gtags之类的工具我们可以很快从原始码中找到它只有一个DLL的函式这对我们而言真是一个好消息 而这个函式便是上述的winampGetMediaLibraryPlugin 。 系统多会采用相同的架构处理插件程式 如果经验不够的话也许无法直接猜出这个函式的作用。 不过如果你是个有经验的程式人多半能从函式所回传的结构猜出这个函式实际的用途。而事实上当你已经知道它是一个插件程式时就应该要明白它可能采用的就是许多系统都采用的相同架构处理插件程式。 当一个系统采用所谓插件形式的架构时它通常不会知道它的插件究竟会怎么实作实作什么功能。它只会规范插件程式需要满足某个特定介面。当系统初始化时所有的插件都可以依循相同的方式向系统注册合法宣示自己的存在。 虽然系统并不确切知道插件会有什么行为展现但是因为它制定了一个标准的介面所以系统仍然可以预期每个插件能够处理的动作类型。这些动作具体上怎么执行对系统来说并不重要。这也正是物件导向程式设计中的“多型”观念。 随着实务经验归纳常见的架构模式 我想表达的重点是当你“涉世越深”之后所接触的架构越多就越能触类旁通。只需要瞧上几眼就能明白系统所用的架构自然就能够直接联想到其中可能存在的角色以及角色间的关系。 像上述的插件程式手法时常可以在许多允许“外挂”程式码的系统中看到。所以有经验的阅读者多半能够立即反应知道像这样的系统的软件应该是让每个插件程式都写成DLL的函式库。 而每个插件的DLL的函式库中都必须提供winampGetMediaLibraryPlugin 这个函式如果你熟悉的Windows的程式设计你会知道这是利用加载 和GetProcAddress 来达成的一种多型手法 。如果你熟悉设计模式你更会知道这是简单工厂方法这个设计模式的运用。 winampGetMediaLibraryPlugin 所回传的winampMediaLibraryPlugin结构正好就描述了每个AOL的Winamp插件的实作内容。 善用名称可加速了解 利用gtags这个工具我们立即发现这个插件它所定义的初始化退出 PluginMessageProc这三个名称都是函式名称。这暗示在多型的作用下它们都是在某些时间点会由AOL的Winamp核心本体呼叫的函式。 名称及命名惯例是很重要的。看到“初始化” 我们会知道它的作用多半是进行初始化的动作而“退出”大概就是结束时处理函式而PluginMessageProc多半就是各种讯息的处理常式过程通常是程序的简写所以PluginMessageProc意指插件讯息程序了。 “望文生义”很重要我们看到函式的名称就可以猜想到它所代表的作用例如当AOL的Winamp尝试着初始化一个插件时它会呼叫这个结构中的初始化函式以便让每个插件程式有机会初始化自己;当AOL的Winamp打算结束自己或结束某个插件的执行时便会呼叫退出函式。当AOL的Winamp要和插件程式沟通时它会发送各种不同的讯息至插件而插件程式必须对此做出回应。 我们甚至不需要检视这几个函式的内容就可以做出推测而这样的假设事实上也是正确的。 阅读他人的程式码 5 -找到程式入口再由上而下抽丝剥茧 根据需要决定展开的层数或展开特定节点并记录树状结构然后适度忽略不需要了解的细节─这是一个很重要的态度。因为你不会一次就需要所有的细节阅读都是有目的的每次的阅读也许都在探索程式中不同的区域。 探索系统架构的第一步就是找到程式的入口点。找到入口点后多半采取由上而下自上而下的方式由最外层的结构一层一层逐渐探索越来越多的细节。 我们的开发团队曾针对AOL的Winamp的iPod的插件进行阅读及探索不仅找到入口点也找出并理解它最根本的基础架构。从这个入口点可以往下再展开一层分别找到三个重要的组成及其意义 ●的init 初始化动作 ●退出 终止化动作 ● PluginMessageProc 以讯息的方式处理程式所必须处理的各种事件 展开的同时随手记录树状结构 当我们从一个入口点找到三个分支后可以顺着每个分支再展开一层所以分别继续阅读的init 退出以及PluginMessageProc的内容并试着再展开一层。阅读的同时你可以在文件中试着记录展开的树状结构。 ●的init 初始化动作 ● itunesdb_init_cc 建立存取iTunes的数据库的同步物件 ●初始化资料结构 ●初始化的GUI元素 ●载入设定 ●建立日志档 ● autoDetectIpod 侦测的iPod插入的执行绪 ●退出 终止化动作 ● itunesdb_del_cc 终止存取iTunes的数据库的同步物件 ●关闭日志档 ●终止化图形用户界面元素 ● PluginMessageProc 以讯息的方式处理程式所必须面临的各种事件 ●执行所连接之苹果的MessageProc 这部分必须要留意几个重点。首先应该一边阅读一边记录文件。因为人的记忆力通常有限对于陌生的事物更是容易遗忘因此边阅读边记录是很好的辅助。 再者因为我们采取由上而下的方式从一个点再分支出去成为多个点因此通常也会以树状的方式记录。除此之外每次只试着往下探索一层。从的init 来看你便会明白。 以下试着摘要的init 的内容 诠释的init itunesdb_init_cc ; currentiPod 空; 苹果新C_ItemList ; ...略 conf_file 字符* SendMessage plugin.hwndWinampParent WM_WA_IPC 0 IPC_GETINIFILE ; m_treeview GetDlgItem plugin.hwnd LibraryParent 0x3fd ; / /这个数字实际上是魔术 ...略 g_detectAll GetPrivateProfileInt “ ml_ipod ” “ detectAll ” 0 conf_file 0 ; ...略 g_log GetPrivateProfileInt “ ml_ipod ” “日志” 0 conf_file 0 ; ...略 g_logfile 打开 g_logfilepath 有“ A ” ; ...略 autoDetectIpod ; 返回0 ; 因为我们只试着多探索一层而目的是希望发掘出下一层的子动作。所以在的init 中看到像“ itunesdb_init_cc ; ”这样的函式呼叫动作时我们知道它是在初始化 之下的一个独立子动作所以可以直接将它列入。但是当看到如下的程式行 currentiPod 空; 苹果新C_ItemList ; 我们并不会将它视为的init 下的一个独立的子动作。因为好几行程式才构成一个具有独立抽象意义的子动作。例如以上这两行构成了一个独立的抽象意义也就是初始化所需的资料结构。 理论上原来的程式撰写者有可能撰写一个叫做init_data_structure 的函式包含这两行程式码。这样做可读性更高然而基于种种理由原作者并没有这么做。身为阅读者必须自行解读将这几行合并成单一个子动作并赋予它一个独立的意义─ ─初始化资料结构。 无法望文生义的函式先试着预看一层 对于某些不明作用的函式叫用不是望其文便能生其义的。当我们看到“ itunesdb_init_cc ”这个名称时我们或许能从“ itunesdb_init ”的字眼意识到这个函式和苹果所采用的的iTunes数据库的初始化有关但“循环”却实在令人费解。为了理解这一层某个子动作的真实意义有时免不了要往前多看一层。 原来它是用来初始化同步化机制用的物件。作用在于这程式一定是用了某个内部的资料结构来储存的iTunes数据库而这资料结构有可能被多执行绪存取所以必须以同步物件此处是视窗的临界区加以保护。 所以说当我们试着以树状的方式逐一展开每个动作的子动作时有时必须多看一层才能真正了解子动作的意义。因为有了这样的动作我们可以在展开树状结构中为itunesdb_init_cc 附上补充说明建立存取iTunes的数据库的同步物件。这么一来当我们在检视自己所写下的树状结构时就能轻易一目了然的理解每个子动作的真正作用。 根据需要了解的粒度决定展开的层数 我们究竟需要展开多少层呢这个问题和阅读程式码时所需的“粒度粒度 ”有关。如果我们只是需要概括性的了解那么也许展开两层或三层就能够对程式有基础的认识。倘若需要更深入的了解就会需要展开更多的层次才行。 有时候你并不是一视同仁地针对每个动作都展开到相同深度的层次。也许你会基于特殊的需求专门针对特定的动作展开至深层。例如我们阅读AOL的Winamp的iPod插件的程式目录其实是想从中了解究竟应该如何存取的iPod上的iTunes的数据库使我们能够将MP3播放歌曲或播放清单加至此数据库中并于的iPod中播放。 当我们层层探索与分解之后找到了parseIpodDb 从函式名称判断它是我们想要的。因为它代表的正是剖析iPod的数据库正是我们此次阅读的重点也就达成阅读这程式码的目的。 我们强调一种不同的做法在阅读程式码时多半采取由上而下的方式而本文建议了一种记录阅读的方式就是试着记录探索追踪时层层展开的树状结构。你可以视自己需要了解的深入程度再决定要展开的层数。你更可以依据特殊的需要只展开某个特定的节点以探索特定的细目。 适度地忽略不需要了解的细节是一个很重要的态度因为你不会一次就需要所有的细节阅读都是有目的的。每次的阅读也许都在探索程式中不同的区域;而每次探索时你都可以增补树状结构中的某个子结构。渐渐地你就会对这个程式更加的了解。 阅读他人的程式码 6 -阅读的乐趣透过程式码认识作者 即便每个人的写作模式多半受到他人的影响程式人通常还是会融合多种风格而成为自己独有的特色如果你知道作者程式设计的偏好阅读他的程式码就更得心应手。 阅读程式码时多半会采取由上而下抽丝剥茧的方式。透过记录层层展开的树状结构程式人可以逐步地建立起对系统的架构观而且可以依照需要的粒度粒度 决定展开的层次及精致程度。 建立架构观点的认识是最重要的事情。虽然这一系列的文章前提为“阅读他人的程式码” 但我们真正想做的工作并不在于彻底地详读每一行程式码的细节而是想要透过重点式的程式码“摘读” 达到对系统所需程度的了解。每个人在阅读程式码的动机不尽相同需要了解的程度也就有深浅的分别。只有极为少数的情况下你才会需要细读每一行程式码。 阅读程式码是新时代程式人必备的重要技能 这一系列的文章至此已近尾声回顾曾探讨的主题我们首先研究了阅读程式码的动机。尤其在开放原始码的风气如此之盛的情况下妥善利用开放原始码所提供的资源不仅能够更快学习到新的技术同时在原始码版权合适时还可以直接利用现成的程式码大幅地提高开发阶段的生产力。所以阅读程式码俨然成为了新时代程式人必备的重要技能之一。 接着我们提到了阅读程式码前的必要准备包括了对程式语言命名惯例的了解等等。在此之后我们反覆提起了“由上而下”的阅读方向的重要性。 由上而下的阅读方式是因为我们重视架构更胜于细节。从最外层的架构逐一向内探索每往内探索一层我们了解系统的粒度就增加了一个等级。当你识别出系统所用的架构时便能够轻易了解在这个架构下会有的角色以及它们之间的动态及静态的关系。如此一来许多资讯便不言可喻毋需额外花费力气便能够快速理解。 好的名称能够摘要性地点出实体的作用 追踪原始码时固然可以用本来的方式利用编辑器开启所需的档案然后利用编辑器提供的机制阅读但是倘若能够善用工具阅读程式码的效率及品质都能大大提升。在本系列文章中我们介绍了一些工具或许你还可以在坊间找到其他更有用的工具。 我在这一系列的文章中实际带着大家阅读追踪了一个名为ml_pod的开放原始码专案。它是一个AOL的Winamp的iPod的外挂程式。在追踪的过程中我们试着印证这一系列文中所提到的观念及方法。我们采用逐渐开展的树状结构来记录追踪的过程并借以建立起对系统的概观认识。 就原始码的阅读来说之前的讨论涉及了工具面及技巧面。但还有一些主题不在这两个范畴之内例如善用名称赋予你的提示。名称做为隐喻隐喻的作用很大好的名称能够摘要性地点出实体的作用例如我们看到autoDetectIpod 自然而然能够想像它的作用在于自动自动侦测检测的iPod的存在。 我们在展开树状结构时有时候需要预看一层有时却不需要这么做便可得到印证。程式人都会有惯用的名称以及组合名称的方法倘若能够从名称上理解便毋需钻进细节可以省去相当多的时间。例如当我们看到parseIpodDb 时便可以轻易了解它是剖析解析的iPod的资料库 DB 的因此便不需要立即钻进parseIpodDb 中查看底细。 尽管如此能否理解程式人命名的用意和自身的经验以及是否了解原作者的文化背景是息息相关的。 命名本身就是一种文化产物。不同的程式人文化就会衍生出不同的命名文化。当你自己的经验丰富看过及接触过的程式码也多时对于名称的感受及联想的能力自然会有不同。 这种感受和联想的能力究竟应该如何精进很难具体描述。就我个人的经验多观察不同命名体系的差异并且尝试归纳彼此之间的异同有助于更快地提升对名称的感受及联想力。 转换立场理解作者的思考方式 除了工具及技巧之外 “想要阅读程式码得先试着阅读写这个程式码的程式人的心。 ”这句话说来十分抽象或许也令人难以理解。 当你在阅读一段程式码时或许可以试着转换自己的立场从旁观者的角度转换成为写作者的心态揣摩原作者的心理及处境。当你试着设身处地站在他的立场透过他的思考方式来阅读追踪他所写下的程式码将会感觉更加流畅。 许多软体专案都不是由单一程式人所独力完成。因此在这样的专案中便有可能呈现多种不同的风格。 许多专案会由架构师决定主体的架构及运作有既定实施的命名惯例及程式设计需要遵守方针。在多人开发的模式下越是好的软体专案越看不出某程式码片段究竟是由谁所写下的。 不过有些开放原始码的专案往往又整合了其他开放原始码的专案。有的时候也很难求风格的统一便会出现混杂的情况。好比之前提到的ml_pod专案因为程式码中混合了不同的来源而呈现风格不一致的情况。 我在阅读非自己所写的程式码时会观察原作者写作的习惯借以对应到脑中所记忆的多种写作模型。在阅读的过程中读完几行程式码我会试着猜想原作者在写下这段程式码时的心境。他写下这段程式码的用意是什么为什么他会采取这样的写法顺着原作者的思考理路阅读自己的思考才能更贴近对方写作当时的想法。 当你短暂化身为原作者时才能更轻易的理解他所写下的程式码。 如果你能知道原作者的背景程式设计时的偏好阅读他的程式码就更能得心应手了。 从程式码着手认识作者独有的风格进而见贤思齐 我在阅读别人写下的程式码时我会试着猜想原作者究竟是属于那一种“流派”呢每个人都有自己独特的写作模式即便每个人的写作模式多半受到他人的影响─ ─不论是书籍的作者学习过程中的指导者或一同参与专案的同侪但每个程式人通常会融合多种风格而成为自己独有的风格。 物件导向的基本教义派总是会以他心中觉得最优雅的物件导向方式来撰写程式。而阅读惯用善用设计模式的程式人所写下的程式码时不难推想出他会在各种常见的应用情境下套用哪些模式。 有些时候在阅读之初你并不知道原作者的习性跟喜好甚至你也不知道他的功力。但是在阅读之后你会慢慢地从一个程式人所写下的程式码开始认识他。 你或许会在阅读他人的程式码时发现令人拍案叫绝的技巧或设计。你也有可能在阅读的同时发现原作者所留下的缺失或写作时的缺点而暗自警惕于心。这也算是阅读他人程式码时的一项乐趣。 当你从视阅读他人的程式码为畏途转变成为可以从中获取乐趣的时候我想你又进到了另一个境界。