好了,你找到了你想阅读的相关代码了。那么接下来怎么办呢?
阅读的时间到了!但不只是随便看看,不行的。我们要阅读,所以我们可以知道我们正在阅读什么。这是有差别的看。愚蠢地逐行扫描对我们没有任何好处,不是为了对我们待读的代码一知半解,希望我们能够获得一些神奇的理解,或者通过深入、重复的阅读,我们将会“get it”。
在我看来,有三个主要的东西可以阻碍你对别人的代码的理解:
你不明白代码编写的一些规约。 (命名约定,样式约定...)
你不明白特定函数究竟做了什么,因为你从来没有遇到过。
你不明白编程语言本身的一些功能。
我们来看看如何处理这些问题:
编码约定,风格指南以及人们自己发明的东西
在对变量、类或方法命名时,人们并不会使用那么多的约定。有赖于你所使用的语言,一些命名约定可能就是语言本身的一部分(变量或函数应该以此样式命名,因为它就得是这样)。
对于命名约定,我发现人们通常会坚持基础的常识。历久弥新的匈牙利命名法或者是其一小部分现如今仍然在被广泛地运用着。
有时候,人们会向类的每一个成员变量名字前头添加一个小写字母m,以此区别于局部变量。有些人就干脆规定成员变量要使用首字母小写的驼峰命名法来写,而本地变量则全部小写(memberVariablesInCamelCase ),并且单词之间用下划线分割(local_variables_have_underscores)。有时私有函数名以下划线开头(_startWithAnUnderscore),而公共方法则并不这样做(publicMethodsDont),同样是以示区分。
不管约定是如何定义的,都得是清晰并且能快速进行识别的。如果幸运的话,内部知识库里面会有一个公开发行的代码风格指南,这样大家就省事儿了。关键是你得去阅读它。回头读过代码之后再来读一下风格指南。
代码约定不是魔鬼,它们通过帮助你快速地对变量和函数的类型进行归类,来帮助你加快阅读和理解代码的加速。
不过只有在你对约定相当熟悉的前提下,他们才会有加快速度的效果,不这样的话,它们就会在你每次遇到它时通过增加一些额外的沟沟坎坎来弥补你对此的理解。 (“为什么他们现在命名为 mServiceManager 而不是 serviceManager 呢?”)。
最后就是,有时人们会发明自己的风格指南,但并不会在任何地方发表。如果照我的经历来说,要么就是编写代码的人是当时比较匆忙,回头没有写一个风格指南,或者留下任何的记录文件(以防留下了什么蛋疼的BUG到时候没法追溯源头),要么就是这个人太自我——这种人经常对写一个风格指南或者说明一下他们在做什么表示不屑(诸如“我的代码不需要单元测试,因为它没有错误”此类的,对吧!),在这样的情况下,代码会被过度设计,封装得跟一个铅球似的,而且BUG重重。
无论采用哪种方式,使用一些非标准的,不成文的编码约定就是一个很可能会有问题发生的迹象,这时候就当心点吧。
公共函数,随机方法和不良命名
下一个事情是,当你阅读的代码是你从来没有听说过的功能时会减慢你的阅读速度。
如果你退一步,看看这个语言,它是由你能理解的简单的代码块组成的:创建新的变量,分配它们的值,做一些条件逻辑,也许是一些循环,如果你运气很糟,它可能是一些二进制算法。但是假设你的语言学的很好,接下来要知道的是你阅读的功能是做什么的。
为了本文的目的,我们将它们分为两种类型的函数:
1.公共函数(也称为库函数)
2.“业务逻辑”函数。
公共函数或库函数是一类几乎可复用的函数。需要格式化日期?这有一个公共函数可用。解析一些JSON?这还有另一个可用的。
通常这些方法是语言库的标准集合的一部分。有时,他们是某些第三方库的一部分(有开源的,有不开源的)。
如果你从未看到过其中一个特定的内容,那么阅读其文档是很有用的。它需要什么样的输入?它产生什么样的输出?它是否改变输入还是仅使用它?调用函数是否有任何副作用?函数执行完成有多慢/多快?
Utility函数的另一个子类是专门针对你所在代码库的库函数。这些是你正在阅读的代码中每隔五行都会出现的函数,并且似乎被用于所有内容。再强调一次,花点时间了解它是明智的。
接下来是业务逻辑函数。这些就是你代码“土豆”中的肉。他们确实是值得你在此认真阅读的部分。
通读他们但不是逐行。除非你确实需要。
让我来说明下原因。
每当我觉得我需要阅读我正在分析的程序中一部分的一个函数时,我有两个选择。我可以选择深入了解它的每个部分,或者只是选择大概了解。
大概了解意味着我将知道函数的名称,输入变量的名称,速读代码以获得对其功能的高层理解,找出它返回值以及它对程序的其他部分所执行的操作(或全部,在我心里这都是代码的味道),然后继续阅读其他的。
除非我在重构影响这个函数的部分,否则我不会逐行阅读。我不需要这么做!一旦我知道了它的作用,BOOM,我就继续读下一个!
每天有太少的时间让我知道这15000行代码究竟什么功能。我只是想知道标题,了解这本书的框架,并不是阅读完整的故事。
这里有一个例外,那就是由monkey(有证据表明是他们编写的)编写的代码。
如果每个变量都被命名为“thingA,thingB,thingC”,或者比这个更糟的,一半的方法在在一个名为“util”的文件夹中,那么你将为此大动干戈了。这些人通常不知道如何专业地开始编写代码,或者如何组织很多代码,并且起初可能不会被雇用。
在你正确地命名代码时,大多数时候,代码是自带文档的(代码即文档)。如果你不正确地命名,这将是一团乱麻。
这也许是时候更新你自己的简历,并给那些你在 LinkedIn 上收到的招聘人员的邮件回信了。如果你维护的代码超过 30% 是 monkey code(随机代码),那么为了维护这些代码你的薪酬可能有点低。(毕竟你正在阅读这篇文章,这意味着你关心软件和你的工作。同样的情况不能说是为了编写这段代码的人,这代码让你调试到凌晨两点,而他正在睡得正香)。
最后,单元测试非常有助于编写代码。
如果你发现你正在阅读的函数有相应的单元测试代码,通过阅读测试代码可能会更快地了解该函数的功能。
当然,前提是测试代码不是由猴子编写的。
你无法理解编程语言本身的一些机制
我的天呐!什么?你应该为自己感到羞耻!
或者也不需要。
我的意思是开始吧。然后呢?你不知道,所以你要去查找。不要觉得自己很糟糕,可以自我训练一下。
你觉得最好的程序员怎么会这么优秀呢?其实他们只是阅读大量相关的软件和代码,一遍又一遍,直到这成为一种习惯。
所以你从来没有在 Java 中使用过 volatile 关键字,但突然间每一个其他的文件都有一个声明为 volatile 的成员变量?那么这就是去阅读 Java 并发和线程文档的很好的时机,以获得更多的知识。慢慢的,对它的使用将会变得得心应手。
你也从来没有见过有人在 Javascript 中使用绑定参数(如 foo.bind(this, arg1) )?那么这可能是时候更多地了解 JS 闭包了。
事实上,如果你认为在你毕业的那一刻学习就已经结束了,那么一定是某人欺骗了你(或是你自己欺骗自己)。学校(或任何形式你所获得的编程相关的初等教育)给了你基础。其余你需要学习的取决于你自己、取决于你专攻的方向,以及日常工作中遇到的问题。
另外,科技产业也在不断地发展、创新和重塑自身。如今的软件不像十年前那样编写了,甚至与五年前的也不一样。
不要对自己不知道的东西感到沮丧。学会去查找,这会让你变得更聪明,并提升自己的技能。这也是长久发展的唯一路径。