如何编写高质量和可维护的代码

[导读] 我们怎么做才能既不需要写很多注释,又能保证代码易于理解呢?其中一个主要的方法就是让代码自文档化。其优势在于,既不用写注释,又能使得代码易于维护。下面就是三种使得代码自文档化的基本方法:命名:利用名...

我们怎么做才能既不需要写很多注释,又能保证代码易于理解呢?

其中一个主要的方法就是让代码自文档化。其优势在于,既不用写注释,又能使得代码易于维护。

下面就是三种使得代码自文档化的基本方法:

  • 命名:利用名字来解释变量、函数等的目的。
  • 封装函数:将一些特定功能的代码封装成一个函数以明确目的。
  • 引入变量:将表达式插入至专用变量。

这可能看上去很简单,但在实际操作过程中会让人觉得有点棘手。首先你得明白哪些地方有问题以及哪些地方适用这些方法。

除了上面三个以外,还有一些应用范围也比较广的方法:

  • 类和模块接口:将类和模块中的函数暴露出来,让代码更加清晰。
  • 代码分组:用组来区分不同的代码片段。

接下来我们将具体讲一讲如何在实际应用中运用上面这5个方法。

1.命名

先看几个如何运用命名的方法来阐述代码使得其自文档化的例子。

重命名函数

给函数命名通常都不会太难,但是这里面也有一些需要遵循的简单规则:

避免使用含糊的字眼,例如“handle”或“manage”——handleLinks、manageObjects。

使用主动动词——cutGrass、sendFile,以表示函数主动执行。

指定返回值类型——getMagicBullet、READFILE。强类型的语言也可以用类型标识符来表明函数的返回值类型。

重命名变量

指定单位——如果里面有数值参数,那可以加上其单位。例如,用widthPx来取代width以指定宽度的单位是像素。

不要使用快捷键——a和b都不能作为参数名。

封装函数

关于这一点,我们将举几个如何把代码封装成函数的例子。此外,这么做还有一个好处是,可以避免重复代码。

将代码封装成函数

这是最基本的:将代码封装成函数以明确其目的。

猜猜下面这行代码是干什么的:

view
source

print?

var width
= (value - 0.5) * 16;

好像不是很清楚,当然有注释就一清二楚了,但是我们完全可以封装成函数以实现自文档化……

view
source

print?

var width
= emToPixels(value);

function emToPixels(ems)
{

return (ems
- 0.5) * 16;

}

唯一改变的是计算过程被转移到了一个函数里。该函数名明确地表达了它要做什么,这样一来就不必写注释了。而且,如果有需要后面还可以直接调用此函数,一举两得,减少了重复劳动。

用函数表示条件表达式

If语句如果包含多个运算对象,不写注释的话理解起来就比较难。

view
source

print?

if(!el.offsetWidth
|| !el.offsetHeight) {

}

知道上面这代码的目的不?

view
source

print?

function isVisible(el)
{

return el.offsetWidth
&& el.offsetHeight;

}

if(!isVisible(el))
{

}

其实,只要将这些代码封装到一个函数里,那就很容易理解了。

引入变量

最后再讲讲如何引入变量。相较于上面两个方法,这个可能没那么有用,但是无论如何,知道比不知道好。

用变量替换表达式

还是上面这个if语句的例子:

view
source

print?

if(!el.offsetWidth
|| !el.offsetHeight) {

}

这次我们不封装函数,改用引入变量:

view
source

print?

var isVisible
= el.offsetWidth && el.offsetHeight;

if(!isVisible)
{

}

用变量替换程式

我们也可以用来清楚说明复杂程式:

view
source

print?

return a
* b + (c / d);

view
source

print?

var divisor
= c / d;

var multiplier
= a * b;

return multiplier
+ divisor;

类和模块接口

类和模块的接口——也是面向公共的方法和属性——有点像说明如何使用的文档。

看个例子:

view
source

print?

class Box
{

public function setState(state)
{

this.state
= state;

}

public function getState()
{

return this.state;

}

}

这个类也可以包含其他代码。我特意举这个例子是想说明公共接口如何自文档化。

你能说出这个类是如何被调用的吗?很显然,这并不明显。

这两个函数都应该换个合理的名字以表述它们的目的。但即便做到这一点,我们还是不怎么清楚如何使用。然后就需要阅读更多的代码或者翻阅文档。

但是如果我们这样改一下呢……

view
source

print?

class Box
{

public function open()
{

this.state
= open;

}

public function close()
{

this.state
= closed;

}

public function isOpen()
{

return this.state
== open;

}

}

明白多了,是吧?请注意,我们只是改动了公共接口,其内部表达与原先的this.state状态相同。

这么一改,我们一眼看去就知道怎么用。原先那个函数名虽然不错,但是依然让我们觉得云里雾里,还不如后者直截了当。像这样做一个小小的改动产生大大的影响,何乐而不为呢?

代码分组

用组来区分不同的代码片段也是自文档化的一种形式。

例如,像这篇文章中说的那样,我们应该尽可能将变量定义在靠近使用它的地方,并且尽可能将变量分门别类。

这也可以用来指定不同代码组之间的关系,这样更加方便其他人知道他们还需要了解哪些代码组。

看看下面的例子:

view
source

print?

var foo
= 1;

blah()

xyz();

bar(foo);

baz(1337);

quux(foo);

view
source

print?

var foo
= 1;

bar(foo);

quux(foo);

blah()

xyz();

baz(1337);

将foo的所有使用组合放在一起,一眼望去就能知道各种关系。

但是有时候我们不得不在中间调用一些其他函数。所以如果可以那就尽量使用代码分组,如果不可以,那就不要强求。

其他建议

  • 不要自作聪明。看看下面这两个等价的表达式:

view
source

print?

imTricky
&& doMagic();

if(imTricky)
{

doMagic();

}

很显然后者更好。

  • 命名常量:如果代码里面有一些特殊值,那最好给它们命名。var PURPOSE_OF_LIFE = 42;
  • 制定规则:最好遵循相同的命名规则。这样阅读的人就能在参考其他代码的基础上正确猜测出各种事物的含义。

结论

要想能使代码自文档化提高其可维护性是一个非常漫长的历程。每个注释都需要花心力去写,所以尽量精简方可省时省力。

然而,自文档化的代码永远取代不了文档和注释。因为代码在表述上总有其限制,所以写好注释亦是不可或缺的。此外,API文档于类库而言非常重要,因为光靠阅读代码是理解不了的,除非这个类库真的是小得不能再小。

时间: 2024-08-24 03:45:05

如何编写高质量和可维护的代码的相关文章

iOS开发中的那些的约定俗成(1)————《编写高质量iOS与OS X代码的52个有效方法》读书笔记(第一章)

iOS开发中的那些的约定俗成(1) ----<编写高质量iOS与OS X代码的52个有效方法>读书笔记(第一章) 前言 "我要成为一个高产的开发人员.""想要混的好,就得多努力." 写这些东西是因为毕竟看了书,但是看书看过去之后,也许印象不是很深刻,有些东西现在也理解不了,那我就把我理解的,现在就可以用到的东西,简单的写出来就好,让自己今后看到就能明白其中的意思. 还有就是锻炼一下表达,编辑能力,慢慢的提升自己,随时随地的都要有一个锻炼的心. 最后当然就

[iOS笔记]《编写高质量iOS与OS X代码的52个有效方法》:1.熟悉Objective-C

简介: 最近公司项目收尾,可以有时间看看书了.<编写高质量iOS与OS X代码的52个有效方法>这本书讲解了很多iOS开发的技巧和规范,大力推荐! 下面是自己看书时整理的笔记,照惯例先上目录: 目录: 第一章:熟悉Objective-C 第二章:Object.Message.Runtime 第三章:接口与API设计 第四章:Protocol与Category 第五章:内存管理 第六章:Block与GCD 第七章:系统框架 第一章    熟悉Objective-C 第1条:了解Objective

&lt;&lt;Effective Objective-C 2.0 编写高质量iOS与OS X代码的52个有效方法&gt;&gt;笔记-对象、消息、运行期

6.理解属性这一概念 可以用@property 语法来定义对象中所封装的数据. 通过"特质"来指定存储数据所需的正确语义. 在设置属性所对应的实例变量时,一定要遵从该属性所声明的语义. 开发iOS程序时应该使用 nonatomic 属性,因为 atomic 属性会严重影响性能. 7.在对象内部尽量直接访问实例变量 对象内部访问实例变量的两种方法:通过属性访问.直接访问. 这两种写法有一个区别: 由于不进过 Object-C 的"方法派送"步骤,所以直接访问实例变量的

Effective Objective-C 2.0 编写高质量iOS与OS X代码的52个有效方法---1-12条

*在其他语言中,许多工作都有编译器来完成:而在OC中,则要于runtime执行.于是,在测试环境下能正常运行的函数到了工作环境中,也许就会因为处理了无效数据而不能正确执行.避免此类问题的最佳方案当然是一开始就把代码写好. 第一章 熟悉Objective-C 第1条 了解Objective-C语言的起源1.消息与函数调用之间的区别关键区别在于:使用消息结构的语言,其运行时所应执行的代码由运行环境来决定: 而使用函数调用的语言,则由编译器决定.如果范例代码中调用的函数是多态的,那 么在运行时就要按照

《编写高质量iOS与OS X代码的52个有效方法》

一.熟悉Objective-C 1.了解Objective-C语言的起源 2.在类的头文件中尽量少引入其他头文件 3.多用字面量语法,少用与之等价的方法 4.多用类型常量,少用#define预处理指令 5.用枚举值表示状态.选项.状态码 二.对象.消息.运行期 6.理解"属性"这一概念 7.在对象内部尽量直接访问实例变量 8.理解"对象等同性"这一概念 9.以"类族模式"隐藏实现细节 10.在既有类中使用关联对象存放自定义数据 11.理解objc

【iOS】编写高质量iOS代码的52个有效方法

<编写高质量iOS与OS X代码的52个有效方法>的笔记,通读了一遍,感觉印象不是特别深刻,写下笔记记录下吧. 这个数的结构很清晰,5章52条建议,分点介绍了编写高效代码的建议,每点后面有总结,这里简单的记录下这些总结,方便温习. 因时间原因,先写5条,有时间逐渐加进来. 第一章:熟悉oc 1,了解oc起源 oc为C语言添加了面向对象特性,是其超集.oc使用动态绑定的消息结构,也就是说已在运行时才会检查对象类型.接收一条消息之后,究竟应执行何种代码,由运行期环境而非编译器来决定. 理解C语言的

代码质量优先——《编写高质量代码:改善c程序代码的125个建议》

高质量的代码不但可以促进团队合作.减少bug处理.降低维护成本,对程序员自身的成长也是至关重要的.很难想象一个参考<如何编写无法维护的代码>写代码的程序员技术成长的上限有多么低.为了写出高质量的代码,我们需要听取过来人的改善代码质量的经验,<编写高质量代码:改善c程序代码的125个建议>就是一本能让人写出高质量代码的好书. 本书的第三章<程序控制语句应该保持简洁高效>首先用简练的语言介绍了流程控制结构的概念,然后提供了对if.else.for.do-while.swit

编写高质量代码改善C#程序的157个建议——建议26:使用匿名类型存储LINQ查询结果

建议26:使用匿名类型存储LINQ查询结果 从.NET3.0开始,C#开始支持一个新特性:匿名类型.匿名类型有var.赋值运算符和一个非空初始值(或以new开头的初始化项)组成.匿名类型有如下基本特性: 即支持简单类型也指出复杂类型.简单类型必须是一个非空初始值,复杂类型则是一个以new开头的初始化项. 匿名类型的属性是只读的,没有属性设置器,它一旦被初始化就不可更改. 如果两个匿名类型的属性值相同,那么就认为这两个匿名类型相等. 匿名类型可以再循环中用作初始化器. 匿名类型支持智能感知. 匿名

编写高质量代码:Web前端开发修炼之道(一)

最近老大给我们买来一些技术方面的书籍,其实很少搬着一本书好好的完整的看完过,每每看电子档的,也是打游击式的看看这章,瞅瞅那章,在那5本书中挑了一本比较单薄的<编写高质量代码web前端开发修炼之道>,看完觉得不错,它从一个整体架构上来说明如何编写高质量代码,而细处也着重说明一些比较重要的技术点,给人一种从高处俯瞰web开发.很完整的感觉,在这感谢老大,谢谢他让我们不停的进步着.下面是我看书过程中的笔记. 第一章:从网站重构说起 没什么好说的,从一个糟糕的老网页实例说明需要将web的结构,样式和行