我是如何阅读开源项目的源代码的

Posted on Tue 23 Sep 15:36 2014  Modify on Tue 23 Sep 15:36 2014

1
2
3
4
public static void main(String[] args) {
    System.out.println("Hello World!");
    System.out.println("中国欢迎你!");
}

阅读优秀代码是提高开发人员水平的最佳方法,就像一名优秀的小说作者,要写出好的作品,首先要做一名如饥似渴的读者,一个卓越的木匠,得观察大量结构优良的家具。同样地,想要在软件工程这条路上走得更远,也必须阅读大量的代码,吸收不同的风格及其精华,看看前辈已尝试过的方法,增长见识,如果你不能吸收他们的经验知识,那你永远都无法成为一位大师。

在我编程生涯的前几年里,我跟大部分的同行一样都讨厌阅读代码,一方面是阅读代码真的不容易,一方面是逃避承担挫败的打击,一方面是英文太差。但工作总会迫使你去克服这些弱点,幸运地,我坚持了下来,并找到自己行之有效的方法。

工具很重要

"磨刀不误砍柴工"这句话告诉我们欲善其事先利其器的道理,说到底就是一个最核心的问题:工具很重要。平时的工作中要多留意并尝试一些代码工具,学习并利用其便利性强化自己的编程技能。在阅读代码的过程中,我们应当掌握如下的工具:

  • 语法检查工具:用于代码重构过程中进行及时的错误提醒,对于查看使用自己不太擅长的语言书写的代码非常有用。
  • 断点调试工具:用于跟踪并明确代码块的价值,定位关键代码位置及判断代码有效性。在目标逻辑可能发生的位置输出日志或注释部分代码然后检验执行过程是否会出错这两种方法在帮助理解代码时经常会被使用,但很多时候这些原始方法的效率都比较低下,要确定某代码块是否重要,最高效的方法就是设断点来调试,它不但能让我们看到代码块是否执行,还能看到所有运行时变量的动态。
  • 版本控制工具:用于代码处理过程中进行版本迭代,一个好的版本管理工具在这个过程中能够帮助你快速取回前面运行良好的代码,让我们更容易发现自己的重构失误,立于不败之地。

时机很重要

带着问题去阅读代码是我认为最有效的方式,也是最好的时机。阅读代码固然能帮助你提升自己的水平,但如果你只是抱着增值自己的初衷去做这件事,你肯定很快就会因为挫折而放弃。相反,如果这一次阅读将会有助于你解决某个问题,你就有很大的理由坚持下去。不需要让别人推荐值得一读的项目源代码,善于发现并记录心中的问题,寻找解决这些问题的项目,问题将转化为学习的动力。

心态很重要

在现实生活中,我们都在被各种拖延症烦扰着,而拖延症重复发作的主要原因是天生的自卑感作祟,特别是在代码研习上体现得更加明显。我们经常会感叹那些项目的强大,并理所当然地在内心为其树立起一个深不可测的形象,这时候钻出的自我怀疑心态更无形中加深了你对它的恐惧,最后的结果是令自己观望止步。

相反地,如果你注意解读自己,你会发现你并不畏惧那些你曾经写过的代码,哪怕是三年前或更早的代码,哪怕你由于太懒一句注释都没写。虽然这个时间点你很可能已经忘记了当时的思路,但只要你经历过从无到有产出这些代码的过程,在心里对它至少是熟悉并知道它对于你来讲是不成问题的,于是你有勇气及动力去重新温习这些经验,并最终获得积极的回报。实际上我们或多或少都会认为别人写的代码不如自己写得好,虽然这未必是事实,但我们在面对自己的代码时会有优越感是毫无疑问的。这种积极的心理暗示可以提炼出一个结论:“只要是我的代码我就能消化掉”,如果我们可以将别人的代码转化成自己的代码,那这个结论能再次成立吗?

私有化别人的代码

对于如何转化别人的代码,我总结出了以下几种方法,这些方法总而言之就是实现理解对方的代码结构和风格,找出重点的目标。

忽略结节,回归理想化

在工作中,我们常常需要理解一个项目的某个功能点,而不是其所有代码,因此培养自己从庞大代码量的项目中定位哪些代码需要关注的能力很重要。通常在核心功能的周围会包含一些例如输入检验、异常处理、数据解析等条件逻辑,这些代码常常会引导你远离核心代码,打乱你的思路。我相信在项目开发初期都是假定所有条件非常理想的情况下进行的,在这个前提下去开发核心功能会令人更加专注,在核心功能测试通过后才开始考虑各种异常情况及处理办法,毫无疑问后期添加的这些安全性条件限制代码令项目更加健壮,但同时增大了其复杂度。而我们只需要理解核心功能的逻辑,所以我们应该忽略那些细节,再度假定所有条件都非常理想,专注于核心部分,远离那些不属于这个范畴的代码。

化繁为简

在计算机领域乃至处理任何事情上,化繁为简都是一个效果显著的方法。假设你处于一个很复杂的类中,比如Android ViewPager,这个类至少有两千行代码,但你只想去读懂它的滑动切换原理,你可以将不处理滑动的代码提取到一个基类里,然后ViewPager继承它,最后你会发现你已经将两千行代码的繁复简化为三百行,你肯定有自信甚至狂妄你能读懂这点代码。从战略的高度上看这三百行代码,足以让我们藐视它,但从战术的高度上看,它却是至关重要的,这正是毛主席在长期的武装斗争后总结出的精粹:“战略上藐视对手,战术上重视对手”。让无关逻辑代码离开你的视线,用军事角度看可以理解为分割围歼战术,先砍掉细枝末节,集中优势力量于重点部分。

在这个过程中如何判断代码可忽略就显得很重要,我的经验是,通过对代码块实施断点调试,找出哪些代码或方法在你需要了解的功能中是不会被调用的,随即清除这些代码(删除或置于基类),并级联清除依赖这些代码的代码。重复这个步骤,直到所有代码都不可或缺为止。这个精简的过程同时能带你粗略认识一下源代码的调用流程,对准确判断代码的用途是很有好处的。

抽取为独立单元

有时候你想理解的核心功能需要满足复杂的条件或通过复杂的操作才能启用,这种情况令你难以调试目标代码。为了提高阅读效率,我们可以通过迁移核心代码到一个新项目或分支上来避免受到其它模块的影响。如果功能需要提供数据去调用的,就自己手动模拟数据;需要操作步骤触发的,就想办法用代码模拟或简化,努力令新项目能够独立地生存,在新项目中调试并研习代码会让你事半功倍。

抽取逻辑到新位置的方法也可以避免当你在大项目中开发时,调试小功能却被其它不相关的模块编译代码带来的额外损耗所影响。假设每次编译浪费你三秒,那一小时估计你也做不了什么。如果你是一个这么有”耐心“的人,愿意等这三秒,那只能证明你没有开动脑筋去思考如何提高工作效率。

寻找程序入口

通过找入口的方式看代码是最有效的方法,一般开源的项目都会提供测试用例,这些用例的代码量往往很少并且点中关键位置。而对于一些有输出效果或者处理结果的项目,可以充分利用IDE强大的文本搜索功能去查找哪些文件包含了你看到的效果中显示的文本信息甚至图片,再查找这些文件的引用者,一直往前推去发现入口类。比如说项目在终端输出了"Hello World"文本,那你就在源代码里搜索这个文本,我相信不出几个回合,你就能到达核心代码。文本搜索定位代码也是我最为推崇最直截了当的学习方式,它不但能节省大量的查找时间,还能免除前述几种方法均需要构建项目的烦恼。

我的实践

作为记录的一部分,以下是我自己阅读Java项目的操作手段,当中或许透着霸气与不敬,但只要落实结果就无可厚非。在三种工具的选择方面,IntelliJ IDEA将作为调试及语法工具,而Git作为一个强大的本地化版本管理工具,将在此后的代码阅读中充当非常得力的角色。

第一步当然是获取源代码,然后导入到IDEA中建立project,并使用Git作第一次提交,这次提交可视为备份初始的源代码版本,以免后期改出问题时需要对比自己的改动跟原版的区别来查错。然后开始尝试运行代码,确保代码运行无误后作第二次提交,尽量使自己的阅读过程基于一个可运行版本,这样无论你如何重构整理都不用担心,有差错时进行对比就能够快速解决。在确定代码是可运行后,接下来进入代码整理工作。使用IDEA的Optimize Import,Reformat,Rearrange package功能格式化所有代码,然后浏览每个代码文件及分包结构,看到不喜欢的写法或者IDE给出优化建议时,马上进行改正,看到一些可能你不需要理解的辅助功能点或操作时,立即动手删除它。遇到无法解决的错误时,粗略看看代码,与需要了解的业务无关的就删除它,如果有关就看看是否能够解决或寻找替代方案,实在没办法就先注释掉。在研究某部分代码时可为其添加一些注释,这些逐步添加的小注释将转化为你深入的动力。整个过程必须伴随着测试,每改动一次代码结构肯定难免会犯错,因此及时测试你的重构很重要。

完成以上的操作后,这份别人的代码很大程度上已经成为了你的代码。你已经将其改为令你舒服的阅读风格,大致理解了某些逻辑的思路,分清了项目的层次结构,培养了对代码的熟悉感,为代码注入了你的因素,给了自己一个很强的心理暗示:代码是我的了。此时再开始研究代码的原理,我相信会比拿到代码后就钻进去会更高效。

总结

综上所述,理解代码最好的方法就是染指对方的代码,简单粗暴占为已有。我个性鲜明的性格造就了我在阅读源代码时的行事作风,也令我养成了严谨的代码习惯,并在工作中展示出来。比如我热衷于删除无用的代码、不允许代码间隔的换行太多、代码缩进必须合理、格式一定要良好、绝不写重复的代码、坚持命名必须有意义。这些完美化的原则体现在我的代码中,对于别人的代码,如果不符合我的习惯,我会在开始理解其逻辑前先彻底改为令我觉得舒服的样子。我会改正我认为命名不合理的地方,删除多余的引用代码,必要时重新整理目录结构,所有的这些工作都只为取悦自己。

必须承认的是,我们的风格以及习惯并不总是对的,这就需要我们在实践中不断完善自己,如果你能不断发现自己的一些错误的想法,那证明你处于成长的阶段。多看大师的代码,学习他们的命名习惯、分包方式、代码层次结构,丰富自己的英文词汇量。这些经验在以后的阅读代码生涯中,你会修炼成看一个名称就知道它大概会做什么的本事,同时对提高你在英文世界中获取知识的能力有巨大的帮助。在你有相当的阅读代码经验积累后,你会发现你的风格跟大师趋于一致,不再由于不习惯他们的写法而纠结。如果你写的代码令阅读者感到舒服,那你也开始迈入大师的行列。