如何快速有效地阅读别人的代码-2

如何阅读代码

第一步,先看全局

阅读代码之前,首先我们要用上帝视角去看源码,用上帝视角目的在于去了解这个组件的全貌。

我们要知道项目主要是用来干嘛的,因为这是项目的终极目标。

例如,对于 logback 而言,它的用途就是打日志。而它所有的代码无论多复杂,终极目标就是要让 logback 能健壮高效的打印出日志来。

然后需要了解系统的层次结构,理出项目的核心脉络。有了核心脉络,我们就能把有限的时间用在阅读最有价值的代码上。

如果项目的官方文档有架构图,那么就从官方的架构图去了解项目的整体架构。如果文档中没有架构图,就去搜一下有没有民间大神画出来,如果还没有,可以根据官方文档的描述,自己画出来架构图。

由于logback官方没有提供架构图,我根据文档大概画了一个架构图。

img

第二步、运行起来

摸清楚了系统的核心脉络,我们还需要把项目运行起来。

还是回到 logback 的例子上。

当我们能成功运行 logback 后,其必然存在了一个 logback.xml 文件,否则无法运行。

这个 logback.xml 文件其实对于我们看源码非常重要,它点出了 logback 需要的关键元素。

并且,如果读源码遇到了困惑,明白了这个配置文件,就能有效帮助我们跨过障碍。后面谈到如何具体的读源码时再细说。

img

上面是一个基本的 logback 配置,里面列出了 logback 运行需要的关键组件。

而且,我们读的开源项目往往都很复杂。最典型的有三种情况:

  • 方法变量不知其意
  • 逻辑跳转绕来绕去
  • 封装对象层次太深

而以上的情况,都只能通过代码调试才能解决。

第三步、抽丝剥茧

全貌、核心脉络知道了,项目运行起来了,你心里说,这下我要读代码了吧?

错,你还差一步,那就是细化目标。

我们读源代码的目的有三类: 1. 提升自己 2. 修复 bug 3. 添加新功能

但是,这些目的过于模糊了。提升自己,那读哪些代码能提升自己?修复 bug,读哪些代码能修复 bug?添加新功能,读哪些代码能把新功能加上?

所以,得把这些有效的代码选出来。如何选呢?

当我们从事开发工作,听得最多的一件事就是把问题分解:把大问题分解成小问题,分而克之。

选择并阅读有效代码也是一样的。

对于过大的代码量,过多的功能,我们紧要的一件事儿就是把比较模糊的目标分解成能具体落地的精准的小目标。这些小目标对应到项目中,其实就是项目的一个一个的业务流程。

比如我们想给 logback 添加个新功能,能让公司的日志打印出统一的固定格式。看看我们如何做:

1. 纵向分解

纵向分解就是在我们已知的架构图上分解出来一条条纵向的业务流程。

由于我们想统一公司的日志格式,那肯定就需要在打印到文件前,把日志内容格式化好。所以,业务流程就应该选择从应用日志调用 logback 打印日志开始,一直到日志内容输出到目标文件结束的业务流程。

img

2. 横向扩展

横向扩展定下了我们如何组合业务流程,从而可以完整的达成咱们开始定下的大目标。

img

比如,这里就可以定下在看完 logback 打印日志的流程后,再去看看 logback 的日志是如何切换的。

第四步、读代码的技巧

好了,现在我们终于要开始看代码了。

但是看代码也是要讲究技巧的,并不是上来就瞎翻瞎看。在读代码的时候,我们还需要掌握几个技巧:

划重点了!

划重点了!

划重点了!

技巧一:代码一定跳着看

有件事我们得明白,不是所有的代码都值得仔细看的。我们最优先的,就是看正向流程的,核心的代码,其余代码皆可以跳过。

可以跳过的代码大概有:

  • 判断异常输入的代码——这类代码对咱们理解系统意义不大,等到以后想提升自己编码能力的时候,可以回头专门找一些优秀的代码集中学。

img

  • 出错处理和异常状态处理的代码——和上面理由一样。

img

  • 数据处理的代码——往往就是解析输入数据,包装输出数据,有些时候还用 DTO 或者 DAO 方式去传递数据。这些代码有些很复杂,也很长,读了之后,耗费精力、扰乱思维不说,往往对掌握项目原理毫无帮助,务必跳过。

img

  • 底层交互的代码——老实讲,底层交互技术含量是很高的,需要很多的底层知识。一时半会儿也无法弥补,而且一旦读不懂了,对信心打击很大,建议跳过。

img

技巧二:调用关系需确定

在看代码的时候,有一些方式会严重我们读代码。

如果你一旦读代码发现你找不到后续流程了,就得考虑考虑,作者是不是用了非顺序调用方式去调用后续方法或者对象。

一般来说,开发人员常用以下几种方式做非顺序调用:

  • 通过中间件继续后续流程,比如 MQ
  • 通过异步方式继续后续流程,比如 Future 模式、Promises 模式
  • 通过回调方式继续后续流程
  • 通过代理委托方式继续后续流程,比如动态代理
  • 通过依赖注入方式继续后续流程,比如 Spring 的 autowired 注解

这些非顺序调用会严重影响我们阅读代码。而对于这几种情况,解决的办法大概有两种:

  • 直接猜——其实后续流程我们在做业务流程映射到实际的代码对象的时候已经大概知道了,如果是接口,我们看看实现类不多,就可以大概挨个看下,一般都能猜着是哪个。
  • 运行起来调试下——这种办法是很普遍的,对任何不确定的任何事情,其实都可以用这个方式。

技巧三:超难算法放最后

对于某些开源项目,它会采用很多经典的算法。很经典,当然也很难。

但是,对于理解整体项目来说,这些算法会严重阻碍我们的进程。我建议这些算法,可以先记下来位置。在后续集中就着算法资料,慢慢理解。

img

上面是 logback 日志文件分割的算法,在理解业务流程时,不建议马上去理解算法,可以放在后面自己另外定个目标理解。

以上就是我多年来一直沿用的代码阅读套路。