




已阅读5页,还剩38页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
如何看懂源代码如何看懂源代码 分析源代码方法分析源代码方法 我们在写程式时 有不少时间都是在看别人的我们在写程式时 有不少时间都是在看别人的代码 例如看小组的代码 看小组整合的守则 若一开始 没规划怎么看 就会 噜看噜苦 台语 不管是参考也好 从开源抓下来研究也好 为了了 解箇中含意 在有限的时间下 不免会对庞大的源 代码解读感到压力 网路上有一篇关于分析看代码的方法 做为程式设 计师的您 不妨参考看看 换个角度来分析 也能更有效率的解读你想要的 程式码片段 六个章节 六个章节 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 框架 以及像 Hib ernate 的这样的资料存取框架 它们都可以视为 最主要的架构下的较次级架构 而各个应用系统 甚至有可能在 Struts 的及休眠之下 建立自有的 更次级的架构 也就是说 当我们谈到 架构 这样的观念时 必须 要有层次感 而不论是那一层级的架构 都会定义 出各自的角色 以及角色间的关系 对阅读者来说 相较于直接切入最细微的单一角色行为 不如了 解某个特定的架构中 究竟存在多少角色 以及这 些角色之间的互动模式 比较能够帮助我们了解整 个系统的运作方式 这是一个很重要的关键 当你试着进到最细节处之 前 应该先试着找出参与的角色 及他们之间的关 系 例如 对事件驱动式的架构而言 有 3 个很 重要的角色 一个是事件处理的分派器 事件调度 一个是事件产生者 事件发生器 另一个 则是事件处理器 事件处理程序 事件产生器产生事件 并送至事件分派器 而事件 分派器负责找出各事件相对应的事件处理器 并且 转交该事件 并命令事件处理器加以处理 像的图 形用户界面的 Windows 应用程式 便是采用事件 驱动式的架构 当你知道此类的应用程式皆为事件驱动式的架构时 你便可以进一步得知 在这样的架构下会有 3 种主要的角色 虽然也许还不清楚整个系统中 究 竟会需要处理多少事件的类型 但对你而言 已经 建立了对系统全貌最概观的认识 虽然你还不清楚所有的细节 但诸如确切会有那些 事件类型之类的资讯 在此刻还不重要 不要忘 了 我们采取的是由上而下的方式 要先摸清楚主 建筑结构 至于壁纸的花色怎么处理 那是到了尾 声时才会做的事 探索架构的第一件事 找出系统如何初始化探索架构的第一件事 找出系统如何初始化 有经验的程式人 对于时常被运用的架构都很熟悉 常常只需要瞧上几眼 就能明白一个系统所用的 架构 自然就能够直接联想到其中会存在的角色 以及角色间的关系 然而 并不是每个系统所用的 架构 都是大众所熟悉 或是一眼能够望穿的 这 时候 你需要探索 目标同样要放在界定其中的角 色 以及角色间的静态 动态关系 不论某个系统所采用的架构是否为大部分人所熟知 的 在试着探索一个系统的长相时 我们应该找出 来几个答案 了解在它所用的架构下 下列这件事 是如何被完成的 一 系统如何初始化 二 与这 个系统相接的其他系统 或使用者 有那些 而相 接的介面又是什么 三 系统如何反应各种事件 四 系统如何处理各种异常及错误 系统如何初始化是很重要的一件事 因为初始化是 为了接下来的所有事物而做的准备 从初始化的方 式 内容 能知道系统做了什么准备 对于系统会 有什么行为展现 也就能得窥一二了 之所以要了 解与系统相接的其他系统 或使用者 为的是 要界定出系统的边界 其他的系统可能会提供输入 给我们所探索的系统 也可能接收来自这系统的输 出 了解这边界所在 才能确定系统的外观 而系统所反应的事件类型 以及如何反应 基本上 就代表着系统本身的主要行为模式 最后 我们必 须了解系统处理异常及错误的方式 这同样也是系 统的重要行为 但容易被忽略 之前 我们提到必 须先具备一个系统的语言基础 才能够进一步加以 阅读 而在本文中 我们的重点放在 要了解一个 系统 最好是采取由上至下的方式 先试着捕捉系 统架构性的观念 不要过早钻进细节 因为那通常 对于你了解全貌 没有多大的帮助 阅读他人的程式码 阅读他人的程式码 3 优质工具在手 读懂优质工具在手 读懂 程式非难事程式非难事 系统的复杂度往往超过人脑的负荷 阅读程式码的 时候 你会需要更多工具提供协助 使用好的整合 式开发环境 IDE 的或文字编辑器 就能提供 最基本的帮助 阅读程式码的动作 可以是很原始的 利用最简单 的文字编辑器 逐一开启原始码 然后凭借着一己 的组织能力 在不同的程式码间跳跃 拼凑出脑中 想要构建的图像 不过 系统的复杂度往往超过人脑的负荷 阅读程 式码的时候 你会需要更多工具提供协助 使用好 的整合式开发环境 IDE 的或文字编辑器 就 能提供最基本的帮助 善用文字编辑器或善用文字编辑器或 IDE 中 加速解读程式码中 加速解读程式码 许多文字编辑器提供了常见程式语言的语法及关键 字标示功能 这对于阅读来说 绝对能够起很大的 作用 有些文字编辑器 例如我常用的编辑器及偶 而使用的记事本 甚至能够自动列出某个 原始档中所有定义的函式清单 更允许你直接从清 单中选择函式 直接跳跃到该函式的定义位置 这 对于阅读程式码的人来说 就提供了极佳的便利性 因为在阅读程式码时 最常做的事 就是随着程式 中的某个控制流 将阅读的重心 从某个函式移至 它所呼叫的另一个函式 所以对程式人来说 阅读 程式码时最常做的事之一就是 找出某个函式位在 那一个原始档里 接着找到该函式所在的位置 好的的 IDE 能够提供的协助就更多了 有些能够 自动呈现一些额外的资讯 最有用的莫过于函式的 原型宣告了 例如 有些的 IDE 支援当游标停留 在某函式名称上一段时间后 它会以提示的方式显 示该函式的原型宣告 对阅读程式码的人来说 在看到程式码中呼叫到某 个函式时 可以直接利用这样的支援 立即取得和 这个函式有关的原型资讯 马上就能知道呼叫该函 式所传入的各个引数的意义 而不必等到将该函式 的定义位置找出后 才能明白这件事 grep 按 读者 推荐来源透视 是一个基本而极按 读者 推荐来源透视 是一个基本而极 为有用的工具为有用的工具 除了选用好的文字编辑器或的 IDE 之外 还有一 个基本 但却极为有用的工具 它就是 grep 按 熟悉的 Unix 作业系统的程式人 对 grep 按这个公 用程式多半都不陌生 grep 按最大的用途 在于 它允许我们搜寻某个目录 包括递回进入所有子目 录 中所有指定档案 是否有符合指定条件 常数 字串或正规表示式 档案 倘若有的话 则能帮你指出所在的位置 这在阅读 程式码时的作用极大 当我们随着阅读的脚步 遇 上了任何一个不认识 但自认为重要的类别 函式 资料结构定义或变数 我们就得找出它究竟位在 这茫茫程式码海中的何处 才能将这个图块从未知 变为已知 grep 按之所以好用 就是在于当我们发现某个未 知的事物时 可以轻易地利用它找出这个未知的事 物究竟位在何方 此外 虽说 grep 按是 Unix 系统 的标准公用程式之一 但是像视窗这样子的平台 也有各种类型的 grep 按程式 对于在视窗环境工 作的程式人来说 可以自行选用觉得称手的工具 gtags 可建立索引 让搜寻更有效率可建立索引 让搜寻更有效率 grep 按虽然好用 但是仍然有一些不足之处 第 一个缺点在于它并不会为所搜寻的原始码档案索引 每当你搜寻时 它都会逐一地找出所有的档案 并且读取其中的所有内容 过滤出满足指定条件的 档案 当专案的原始码数量太大时 就会产生搜寻 效率不高的问题 第二个缺点是它只是一个单纯的文字档搜寻工具 本身并不会剖析原始码所对应的语言语法 当我们 只想针对 函式 名称进行搜寻时 它有可能将注解 中含有该名称的原始码 也一并找了出来 针对 grep 按的缺点 打算阅读他人程式码的程式 人 可以考虑使用像是 gtags 这样子的工具 gta gs 是源代码的 GNU 全局标记系统 它不只搜寻 文字层次 而且因为具备了各种语言的语法剖析器 所以在搜寻时 可以只针对和语言有关的元素 例如类别名称 函式名称等 而且 它能针对原始码的内容进行索引 这意谓一 旦建好索引之后 每次搜寻的动作 都毋需重新读 取所有原始码的内容并逐一搜寻 只需要以现成的 索引结构为基础 即可有效率的寻找关键段落 gtags 提供了基于命令列的程式 让你指定原始码 所在的目录执行建立索引的动作 它同时也提供程 式让你得如同操作 grep 按一般 针对索引结构进 行搜寻及检索 它提供了许多有用的检索方式 例 如找出专案中定义某个资料结构的档案及定义所在 的行号 或者是找出专案中所有引用某资料结构的 档案 以及引用处的行号 这么一来 你就可以轻易地针对阅读程式码时的需 求予以检索 相较于 grep 按所能提供的支援 gt ags 这样的工具 简直是强大许多 再搭配再搭配 htags 制作的制作的 HTML 文件 更是如虎添翼文件 更是如虎添翼 还有一个绝对需要一提的工具 这个叫做 htags 的 工具 能够帮你将已制作完成的索引结构 制作成 为一组相互参考的的 HTML 文件 基本上 利用 这样的的 HTML 文件阅读程式码 比起单纯地直 接阅读原始码 来得更有结构 原因是阅读程式码 时 这样的的 HTML 文件 已经为你建立起在各 个原始码档案片段间跳跃的链结 例如 图一是针 对一个有名的开放原始码专案 ffmpeg 由 gtags 所产生出来的的 HTML 文件首页的一部分 htags 工具首先为你找出所有定义的 Main 函式的档案 并且列出所在的函式 找出的 Main 函式 时常是阅读程式码的第一步 因为主 要 函式是程式的主要入口点 所有的动作皆 由此启动 它是一切事物的源头 凭借 htags 制作的的 HTML 文件 你可以轻易地 点击超连结 直接进到的 Main 函式所在的 程式码片段 如图二 当我们检视上述原始码时 发现 av register all 是个陌生 无法了解的事物 而想要搞懂它 究竟是什么 可以再继续点击这个函式 如图三 这真是太方便了 阅读至此 你会猛然发现 gta gs 仿佛就是为了阅读程式码而专门量身打造的利 器 阅读他人的程式码 阅读他人的程式码 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 系统多会采用相同的架构处理插件程式系统多会采用相同的架构处理插件程式 如果经验不够的话 也许无法直接猜出这个函式的作用 不过 如果你是个有经验的程式人 多半能从函式所回 传的结构 猜出这个函式实际的用途 而事实上 当你 已经知道它是一个插件程式时 就应该要明白 它可能 采用的 就是许多系统都采用的相同架构处理插件程式 当一个系统采用所谓插件形式的架构时 它通常不会知 道它的插件究竟会怎么实作 实作什么功能 它只会规 范插件程式需要满足某个特定介面 interface 当系统 初始化时 所有的插件都可以依循相同的方式 向系统 注册 合法宣示自己的存在 虽然系统并不确切知道插件会有什么行为展现 但是因 为它制定了一个标准的介面 所以系统仍然可以预期每 个插件能够处理的动作类型 这些动作具体上怎么执行 对系统来说并不重要 这也正是物件导向程式设计中的 多型 观念 随着实务随着实务经验 归纳常见的架构模式 归纳常见的架构模式 我想表达的重点 是当你 涉世越深 之后 所接触的架 构越多 就越能触类旁通 只需要瞧上几眼 就能明白 系统所用的架构 自然就能够直接联想到其中可能存在 的角色 以及角色间的关系 像上述的插件程式手法 时常可以在许多允许 外挂 程 式码的系统中看到 所以 有经验的阅读者 多半能够 立即反应 知道像这样的系统的软件 应该是让每个插 件程式 都写成 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 x3fd 这个数字实际上是魔术 略 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 cycle 这个名称时 我们或许能从 itunesdb init 的字眼意识到这个函式和苹果所采用的的 iTunes 数据 库的初始化有关 但 循环 却实在令人费解 为了理解 这一层某个子动作的真实意义 有时免不了要往前多看 一层 原来它是用来初始化同步化机制用的物件 作用在于这 程式一定是用了某个内部的资料结构来储存的 iTunes 数据库 而这资料结构有可能被多个执行程序存取 所 以必须以同步物件 此处是视窗的临界区 加以保护 所以说 当我们试着以树状的方式 逐一展开每个动作 的子动作时 有时必须多看一层 才能真正了解子动作 的意义 因为有了这样的动作 我们可以在展开树状结 构中 为 itunesdb init cc 附上补充说明 建 立存取 iTunes 的数据库的同步物件 这么一来 当我 们在检视自己所写下的树状结构时 就能轻易一目了然 的理解每个子动作的真正作用 根据需要了解的粒度 决定展开的层数根据需要了解的粒度 决定展开的层数 我们究竟需要展开多少层呢 这个问题和阅读程式码时 所需的 粒度 粒度 有关 如果我们只是需要概括性 的了解 那么也许展开两层或三层 就能够对程式有基 础的认识 倘若需要更深入的了解 就会需要展开更多 的层次才行 有时候 你并不是一视同仁地针对每个动作 都展开到 相同深度的层次 也许 你会基于特殊的需求 专门针 对特定的动作展开至深层 例如 我们阅读 AOL 的 Winamp 的 iPod 插件的程式目录 其实是想从中了解 究竟应该如何存取的 iPod 上的 iTunes 的数据库 使我 们能够将 MP3 播放歌曲或播放清单加至此数据库中 并于的 iPod 中播放 当我们层层探索与分解之后 找到了 parseIpodDb 从函式名称判断它是我们想要的 因为它代表的 正是剖析 iPod 的数据库 正是我们此次阅读的重点 也就达成阅读这程式码的目的 我们强调一种不同的做法 在阅读程式码时 多半采取 由上而下的方式 而本文建议了一种记录阅读的方式 就是试着记录探索追踪时层层展开的树状结构 你可以 视自己需要 了解的深入程度 再决定要展开的层数 你更可以依据特殊的需要 只展开某个特定的节点 以 探索特定的细目 适度地忽略不需要了解的细节 是一个很重要的态度 因为你不会一次就需要所有的细节 阅读都是有目的的 每次的阅读也许都在探索程式中不同的区域 而每次探 索时 你都可以增补树状结构中的某个子结构 渐渐地 你就会对这个程式更加的了解 阅读他人的程式码 阅读他人的程式码 6 阅读的乐趣 透过程式码阅读的乐趣 透过程式码 认识作者认识作者 即便每个人的写作模式多半受到他人的影响 程式人通 常还是会融合多种风格 而成为自己独有的特色 如果 你知道作者程式设计的偏好 阅读他的程式码就更得心 应手 阅读程式码时 多半会采取由上而下 抽丝剥茧的方式 透过记录层层展开的树状结构 程式人可以逐步地建立 起对系统的架构观 而且可以依照需要的粒度 粒度 决定展开的层次及精致程度 建立架构观点的认识是最重要的事情 虽然这一系列的 文章前提为 阅读他人的程式码 但我们真正想做的工 作 并不在于彻底地详读每一行程式码的细节 而是想 要透过重点式的程式码 摘读 达到对系统所需程度的 了解 每个人在阅读程式码的动机不尽相同 需要了解 的程度也就有深浅的分别 只有极为少数的情况下 你 才会需要细读每一行程式码 阅读程式码是新时代程式人必备的重要技能阅读程式码是新时代程式人必备的重要技能 这一系列的文章至此已近尾声 回顾曾探讨的主题 我 们首先研究了阅读程式码的动机 尤其在开放原始码的 风气如此之盛的情况下 妥善利用开放原始码所提供的 资源 不仅能够更快学习到新的技术 同时在原始码版 权合适时 还可以直接利用现成的程式码 大幅地提高 开发阶段的生产力 所以 阅读程式码俨然成为了新时 代程式人必备的重要技能之一 接着 我们提到了阅读程式码前的必要准备 包括了对 程式语言 命名惯例的了解等等 在此之后 我们反覆 提起了 由上而下 的阅读方向的重要性 由上而下的阅读方式 是因为我们重视架构更胜于细节 从最外层的架构逐一向内探索 每往内探索一层 我们 了解系统的粒度就增加了一个等级 当你识别出系统所 用的架构时 便能够轻易了解在这个架构下会有的角色 以及它们之间的动态及静态的关系 如此一来 许多资 讯便不言可喻 毋需额外花费力气 便能够快速理解 好的名称能够摘要性地点出实体的作用好的名称能够摘要性地点出实体的作用 追踪原始码时 固然可以用本来的方式 利用编辑器开 启所需的档案 然后利用编辑器提供的机制阅读 但是 倘若能够善用工具 阅读程式码的效率及品质都能大大 提升 在本系列文章中 我们介绍了一些工具 或许你 还可以在坊间找到其他更有用的工具 我在这一系列的文章中 实际带着大家阅读 追踪了一 个名为 ml pod 的开放原始码专案 它是一个 AOL 的 Winamp 的
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- project考试试题及答案
- 电缆厂检验知识培训课件
- 电煤知识培训内容摘要模板课件
- 本科线性代数考试题目及答案
- 高热惊厥科普课件
- Nicomol-Standard-生命科学试剂-MCE
- Acedapsone-d8-生命科学试剂-MCE
- MEDI-8852-生命科学试剂-MCE
- 保险学第七版考试题库及答案
- 专升本考试题目及答案
- 我的家乡南阳
- 员工带孩子上班免责协议书
- 住院病历质量评审标准
- 高原病的预防与适应
- 老年健康照护课件
- 设备维修工技能培训
- 马克思主义政治经济学第7章剩余价值的分配
- 新生儿窒息复苏指南
- 成品出货检验报告模板
- 眼的生物化学讲义
- 2023年中考语文一轮复习:语段综合专项练习题汇编(含答案)
评论
0/150
提交评论