源码阅读浅谈
Tips:以下所说的源码释义为:仓库,工程,项目
“源码阅读如解刨”
源码分类
-
从测试的角度
• 项目:有人机交互的界面, 比如:电商网站,app应用(单元测试,主要是端到端测试)
• 库:提供某种功能,比如:traceroute, nodejs, antd-design(单元测试,功能测试,兼容性测试)
• 混合体(超,大型项目):既有支撑库,又有人机交互的界面,比如:chrome, vscode -
从工作(work)的角度
• 业务源码:我们平时工作时,接触到的第一线的业务源码主工程
• 非业务源码:一线业务源码的社区依赖模块、第三方库,相类似功能的社区工程,底层框架代码
阅读源码第一因:
-
对于非业务相关源码而言:
○ 首先,我们阅读的动机首先是更好的使用该框架/库。了解原理,以写出更优雅的业务代码。
○ 其次,遇到API相关的问题,我们没有从它的使用文档,社区知识社区找寻到答案,则需要抽丝剥茧,追根溯源地阅读源码,以找到问题的解决之道
○ 最后,则是为了充实精进我们自己的编程知识库,通过阅读源码的方式,学习该种编程语言的最佳实践,编程思想,奇技淫巧。
-
对于业务相关的代码:
○ 我们的初始目标肯定是为了上手开发/维护该项目
○ 有其他的业务模块用到了该块逻辑,需要简单了解实现技术,以方便排查某业务链路的bug
○ 模仿项目的开发配置与组织结构
下面的内容主要偏向【业务源码、项目源码】的思考
阅读源码进行时
我们要以什么样的遵旨去阅读它?可维护性
那么我们要如何入手源码阅读呢?
-
看新手文档/用户文档/快速开始/wiki/
一般地,这些文档会交代如下内容:项目介绍(包括组织结构,涉及的其他领域知识),项目如何运行起来,项目的技术选型,环境配置注意事项,某些特定(复杂)业务逻辑的文档说明。测试相关文档
-
找到项目的入口文件,启动之。使用以下手段来梳理项目的脉络图。打日志,debugger断点(debugger最重要的功能是获取call stack)。
特别地,对于某些库源码,比如提供UI库的源码,工具箱,没有唯一的入口,则需要以单元测试/功能测试的角度来启动
-
提出问题,并尝试解答,试着修改一些数据和代码,看看有什么变化。
从代码整洁之道出发,一点点的阅读源码,试思考该处为何如此编码,是否为最优解,是否可以精简代码,是否需要添加注释
什么是整洁代码:
1,代码逻辑直接了当,让缺陷难以隐藏 2,尽量减少依赖关系,使之便于维护 3,依据某种分层策略完善错误处理代码 4,性能调至最优,省得引诱别人做没规矩的优化 5,整洁的代码只做一件事 6,简单直接,具有可读性 7,有单元测试和验收测试 8,有意义的命名 9,代码应在字面上表达其含义 10,尽量少的实体:类、方法、函数 11,没有重复代码
《代码整洁之道》298页
-
将思考的答案必要地记录,将未解决的问题跳过。
有时,我们探究很久,然而还是没有找到某问题的解答,这时,不要继续深入探索下去,尝试着先跳过这个点、这个概念、这个过程。继续下面的源码分析。原因在于:在那个场景下,极容易被一叶障目,陷入自己的思维怪圈。且放下它,第二天再来看它,或许会豁然开朗。
从抽象与分层与姿态说起
说到脉络图,我们要如何梳理出呢?这里有一个很好的切入点,即:抽象与分层
抽象与分层是计算机科学中的一种基本思想方法,是降低计算机系统复杂度的有效方法。也可以有效地降低我们阅读源码时的心智负担
抽象是对普遍性的表达,分层则是在恰当的语义层上放置抽象
抽象,本质上是一种泛化与概括的思维方式。分层则基于抽象来进行的,我们对底层的数据和计算过程抽象之后,底层就可以通过调用接口对外提供某种服务,在这个基础上我们又可以进行更高一层的抽象,就这样一层一层的往上构建我们就可以得到内在结构高度自动化
主要有六类抽象:
流程型抽象: 表达应用流程,将单一功能构造成实用的服务
任务型抽象: 使用有限可控的任务执行者集稳定高效地完成源源不断来临的任务
数据处理抽象: 任务的实际内容
结构型抽象: 存储和容纳执行任务所需要的资源、数据集
数据模型抽象: 具有语义关联的数据项聚合体
原子数据抽象: 组成数据的基本数据单位
那么,我们具体要怎么操作呢?
从入口阅读源码时,一边找抽象概念,一边基于抽象概念找对应的分层,然后又基于分层去有的放矢地阅读对应的源码
我们阅读源码,不要试图一口气从项目的入口阅读到最终流程。
阅读,找抽象概念,建立分层,这三者应该相互联动起来。从而最终建立一个完整的项目脉络图
姿态:这里仅仅包含库类型的源码,所谓姿态,可以理解为支撑库的兼容层、适配层代码。不同的环境,有不同的处理细节(姿态),
这里的层和抽象和分层中的“层”不是一回事。
- 兼容层是对某些特定细节代码的功能打平
- 分层是着眼于整个源码工程,以垂直的角度去划分业务逻辑与细节
那我们在阅读源码时,怎么去切入“姿态”呢?
- 当我们阅读到兼容层代码时,我们需要分析不同环境下适配代码的逻辑与结果的一致性。
- 且需要关注到在代码编译(打包)环节,是否对其他环境的代码做了“裁剪处理”
- 以及在进行单元测试/功能测试时,兼容代码的有效处理
兼容层代码更多是对代码组织结构上的最佳实践,比如说,一个涉及到多个环境的库,不去做兼容层代码可以吗?完全是可以的,只不过在后期支持多个环境时,要花更多的精力而已。
源码阅读与编程最佳实践与查漏补缺
我们阅读了源码,有很多收获,以及疑问。然后呢?
- 补充缺失的文档
- 添加必要的注释
- 添加缺失的单元测试,功能测试,并验证之,在这个过程中,我们会更好的了解业务逻辑
- 项目源码最佳实践
单元测试还有这样一个好处:在业务完成后,后续调试bug,我们不需要去关注函数实现是否有边界bug,可以先把注意力放在业务流程本身上的错误以及入口函数的参数的错误