其中一个主要的方法就是让代码自文档化。其优势在于,既不用写注释,又能使得代码易于维护。
下面就是三种使得代码自文档化的基本方法:
这可能看上去很简单,但在实际操作过程中会让人觉得有点棘手。首先你得明白哪些地方有问题以及哪些地方适用这些方法。
除了上面三个以外,还有一些应用范围也比较广的方法:
接下来我们将具体讲一讲如何在实际应用中运用上面这5个方法。
先看几个如何运用命名的方法来阐述代码使得其自文档化的例子。
重命名函数
给函数命名通常都不会太难,但是这里面也有一些需要遵循的简单规则:
避免使用含糊的字眼,例如“handle”或“manage”——handleLinks、manageObjects。
使用主动动词——cutGrass、sendFile,以表示函数主动执行。
指定返回值类型——getMagicBullet、READFILE。强类型的语言也可以用类型标识符来表明函数的返回值类型。
重命名变量
指定单位——如果里面有数值参数,那可以加上其单位。例如,用widthPx来取代width以指定宽度的单位是像素。
不要使用快捷键——a和b都不能作为参数名。
关于这一点,我们将举几个如何把代码封装成函数的例子。此外,这么做还有一个好处是,可以避免重复代码。
将代码封装成函数
这是最基本的:将代码封装成函数以明确其目的。
猜猜下面这行代码是干什么的:
var width = (value - 0.5) * 16;
好像不是很清楚,当然有注释就一清二楚了,但是我们完全可以封装成函数以实现自文档化……
var width = emToPixels(value); function emToPixels(ems) { return (ems - 0.5) * 16; }
唯一改变的是计算过程被转移到了一个函数里。该函数名明确地表达了它要做什么,这样一来就不必写注释了。而且,如果有需要后面还可以直接调用此函数,一举两得,减少了重复劳动。
用函数表示条件表达式
If语句如果包含多个运算对象,不写注释的话理解起来就比较难。
if(!el.offsetWidth || !el.offsetHeight) { }
知道上面这代码的目的不?
function isVisible(el) { return el.offsetWidth && el.offsetHeight; } if(!isVisible(el)) { }
其实,只要将这些代码封装到一个函数里,那就很容易理解了。
最后再讲讲如何引入变量。相较于上面两个方法,这个可能没那么有用,但是无论如何,知道比不知道好。
用变量替换表达式
还是上面这个if语句的例子:
if(!el.offsetWidth || !el.offsetHeight) { }
这次我们不封装函数,改用引入变量:
var isVisible = el.offsetWidth && el.offsetHeight; if(!isVisible) { }
用变量替换程式
我们也可以用来清楚说明复杂程式:
return a * b + (c / d);
var divisor = c / d; var multiplier = a * b; return multiplier + divisor;
类和模块的接口——也是面向公共的方法和属性——有点像说明如何使用的文档。
看个例子:
class Box { public function setState(state) { this.state = state; } public function getState() { return this.state; } }
这个类也可以包含其他代码。我特意举这个例子是想说明公共接口如何自文档化。
你能说出这个类是如何被调用的吗?很显然,这并不明显。
这两个函数都应该换个合理的名字以表述它们的目的。但即便做到这一点,我们还是不怎么清楚如何使用。然后就需要阅读更多的代码或者翻阅文档。
但是如果我们这样改一下呢……
class Box { public function open() { this.state = open; } public function close() { this.state = closed; } public function isOpen() { return this.state == open; } }
明白多了,是吧?请注意,我们只是改动了公共接口,其内部表达与原先的this.state状态相同。
这么一改,我们一眼看去就知道怎么用。原先那个函数名虽然不错,但是依然让我们觉得云里雾里,还不如后者直截了当。像这样做一个小小的改动产生大大的影响,何乐而不为呢?
用组来区分不同的代码片段也是自文档化的一种形式。
例如,像这篇文章中说的那样,我们应该尽可能将变量定义在靠近使用它的地方,并且尽可能将变量分门别类。
这也可以用来指定不同代码组之间的关系,这样更加方便其他人知道他们还需要了解哪些代码组。
看看下面的例子:
var foo = 1; blah() xyz(); bar(foo); baz(1337); quux(foo);
与
var foo = 1; bar(foo); quux(foo); blah() xyz(); baz(1337);
将foo的所有使用组合放在一起,一眼望去就能知道各种关系。
但是有时候我们不得不在中间调用一些其他函数。所以如果可以那就尽量使用代码分组,如果不可以,那就不要强求。
imTricky && doMagic(); if(imTricky) { doMagic(); }
很显然后者更好。
要想能使代码自文档化提高其可维护性是一个非常漫长的历程。每个注释都需要花心力去写,所以尽量精简方可省时省力。
然而,自文档化的代码永远取代不了文档和注释。因为代码在表述上总有其限制,所以写好注释亦是不可或缺的。此外,API文档于类库而言非常重要,因为光靠阅读代码是理解不了的,除非这个类库真的是小得不能再小。