如何熟悉新代码

5.00 avg. rating (98% score) - 3 votes
转载请注明: 吹水小镇 | reetsee.com
原文链接地址: http://blog.reetsee.com/archives/651

每个程序员都会碰到的问题,都有自己的方法。在一周内看完一个大模块近10000行的代码后,我总结一下对自己来说比较有用的经验。

bmwm3

本文首发公众号——吹水小镇


背景

从加入搜索中心到刚才看完代码之前,其实我对当前搜索架构的底层细节了解不多,只知道大体上是要建索引,索引要分库,请求来了之后要切词建语法树,再求交然后打分得到结果。

模块的总体流程和大致架构都有所了解,但具体各个大步骤里面包含哪些小步骤,每个小步骤里用的数据结构长什么样,我都不是很清楚。

因此在年底这个难得的时间,我决定把底层架构代码系统地看一下。由于代码量比较大,我开始思考看代码有没有“捷径”可走?有没有一种方法能既不是走马观花又能节省时间?下面我给出一点经验,下面的步骤可以认为是有顺序的。

了解代码大致流程

这一步其实都会。简单来说就是先了解模块整体的大致流程,再对应到代码中找不同入口,知道大步骤是什么。

举个例子:

  1. 在C++里面就找main函数,看下在main函数里,初始化了哪些对象(例如执行了Init函数);
  2. 这些被初始化的对象最终在哪里被使用,调用的是什么方法;
  3. 如果碰到面条代码(就是所有逻辑写到一个函数,不分小函数),先尝试分出逻辑块,不用分得细;
  4. 最后把上面得到的一个流程的蓝图与自己原本理解的模块架构、流程进行对应,哪一块代码对应的是哪个小模块,哪个逻辑对应的是哪一步数据传输、计算。

从入口函数开始,深度优先遍历

从这一步开始就精看代码,而使用的路径是每遇到一个函数,就进去看,例如server端代码最常见的就是在main函数碰到某个对象的Init,或者对于某个Dispatch函数,下面有并列的几个接口函数,或者对于异步线程,在执行“start”之后直接看它的循环逻辑(通常是Entry函数)。

不要看那些还没使用到的函数以及逻辑。

直接看函数实现细节,跳过变量声明定义

我把这一步简称为“盲人摸象”。

其实最大收获在这一步,我发现大多数情况下不看一个变量、一个class的声明定义其实影响不大,除非你从这个变量名根本没法看出它是做什么的,例如 “a”,”tmp” 这样的变量。

通常需要你系统了解的代码,本身会相对注意编码风格以及变量命名的问题。

我们都知道变量命名得好可以帮助大家理解代码,但我是这次才发现命名良好的变量能够跳过大量不必要看的代码,避免在多个文件之间来回切换,很好地保持了思维的连续性。

举个例子,当你看到这段代码的时候:

m_score_mgr.PushToTopKScores(score);
m_score_mgr.PushToTopKScores(score_list);

你可以不需要急着去看 m_score_mgr 是个什么类型,它的成员有哪些, 只需要知道 m_score_mgr 它有一个方法,可以接收一个分数列表,并且排序出 Top K就行了。

这样当你看到这个下面这行代码时:

top_k_score_list = m_score_mgr.GetTopKScores();

你就很自然想到这一步的输出来自于上面代码的输入。至于这两部之间详细逻辑是怎么样的,可以后面等你需要了解细节的时候再进去看。

复杂的类,理解清楚成员变量发生变化的逻辑

在整个看代码过程中,最难处理的是那种庞大复杂的类,它的成员变量在成员函数中多个地方都有修改以及使用,而这个成员变量本身又包含了复杂的逻辑,在发生改变时,也很难直接看出它的内部状态到底发生了什么变化。
这种其实就没有太好的办法了,要弄清楚类与成员变量之间的关联关系、变化逻辑,自底向上(从成员到类)弄清楚每个方法的具体作用及影响。

最后一步,就是自己重新把代码的逻辑结构描述一遍,知道其中的实现难点以及关键思路。

发表评论

电子邮件地址不会被公开。 必填项已用*标注