本篇主要总结《代码不朽 编写可维护软件的10大要则》中的低层级原则,包括如下内容:
一. 编写短小的代码单元
1. 原则:
- 代码单元的长度应该限制在15行代码以内。
- 你应该编写不超过15行代码的单元,或者将长的单元分解成多个更短的单元,直到每个单元都不超过15行代码。
- 该原则能提高可维护性的原因在于,短小的代码单元易于理解、测试及重用。
2. 动机:
短小的代码单元易于测试、分析、重用。
-
短小的代码单元易于测试
一般来说,短小的代码单元可能只做一件事,而较长的代码单元会尝试做多件事,拥有单一职责的代码更易于测试。 -
短小的代码单元易于分析
相比代码行数较多的代码单元,分析短小单元内部原理所花费时间更少。 -
短小的代码单元易于重用
较长的代码单元试图提供各种具体的实现细节,或者几个功能的固定组合,因此提供的功能范围也更加特定。短小的代码单元职责相对单一,更易于重用。
3. 如何使用本原则:
-
场景一:编写一个新的代码单元时
编写一个新的代码单元时,绝对不要让它超过15行代码。当你写到第15行代码时,你需要开始思考如何添加下一步的功能。它真的属于你当前编写的单元,还是应该有自己的代码单元?如果单元中的代码确实超过15行,需要想办法将它变得更短。 -
场景二:向代码单元中添加新功能时
当你向系统添加新功能时,代码单元开始变得越来越长,需要坚持原则,绝不能超过代码行数的限制。如果超过限制,需要考虑重构代码。
使用重构来实现该原则:
1. 提取方法
2. 将方法替换为方法对象
4. 本节内容总结:
有人可能会质疑代码单元的长度限制在15行以内是否可以实现需求功能。根据SIG的代码检测分析,优秀的项目1-15行代码占比甚至会高于90%。
实际上,要求项目中每个代码单元都不超过15行也不太现实,它只是我们编写代码单元时的目标。每当超过15行时,我们要停下来思考是否必须要超过15行,有何办法可以让代码单元不超过15行。你可以先完整地实现代码单元的功能,然后再停下来思考该如何重构以缩短其长度。如此以来,不仅可以提高代码的可维护性,更能大幅提高个人的编码水平。
二. 编写简单的代码单元
1. 原则:
- 限制每个代码单元分支点的数量不超过4个。
- 应该将复杂的代码单元拆分成多个更简单的单元,避免多个复杂的单元在一起。
- 该原则能提高可维护性的原因在于,分支点越少,代码单元越容易被修改和测试。
2. 动机:
简单的代码单元易于修改、测试。
-
简单的代码单元易于修改
高复杂度的代码单元通常难以理解,更难以修改。 -
简单的代码单元易于测试
代码单元的分支点越多,要覆盖所有分支的测试用例就越多,难以测试。
3. 如何使用本原则:
-
场景一:代码单元本身包含较多会增加分支点的操作符
代码单元包含较多会增加分支点的操作符,如if、case、&&、||、while、for、for...each、catch
等。这说明代码单元逻辑较复杂,需要通过提取方法来降低复杂度。 -
场景二:处理链式条件语句
对于链式的if-then-else语句,或者switch-case语句,每个条件都是互斥的,可以通过引入更合适的数据结构
或引入多态
来降低复杂度。 -
场景三:处理嵌套条件语句
对于嵌套层级多的条件语句,可以通过卫语句 & 提取方法
来降低复杂度。
4. 本节内容总结:
代码单元简单意味着代码单元分支点少,最简单的情况就是整个代码单元顺序执行,没有多余分支。每多一个分支,理解起来相当于要多“压栈”一次,编写测试代码时就需要增加一个测试用例。
圈复杂度是代码复杂性的衡量标准,理解了圈复杂度,就理解了编写简单代码的必要性。
三. 不写重复代码
1. 原则:
- 不要复制代码。
- 你应该编写可重用的、通用的代码,或者调用已有的代码。
- 该原则能提高可维护性的原因在于,如果复制代码,就需要在多个地方修复bug,不仅低效,且容易出错。
重复代码的类型:
-
1类克隆:
含有至少6行相同代码(不包括空行和注释行)。 -
2类克隆:
在语法上相同的两段代码,如下述两段代码:
public void setPageWidthInInches(float newWidth) {
float cmPerInch = 2.54f;
this.pageWidthInCm = newWidth * cmPerInch;
...
}
public void setPageWidthInPoints(float newWidth) {
float cmPerPoint = 0.03527f;
this.pageWidthInCm = newWidth * cmPerPoint;
//其他代码,同setPageWidthInInches中的一样
}
2. 动机:
-
重复代码更加难以分析
倘若问题出在有重复代码的地方,必须查找并分析所有重复的地方,增加了处理难度。 -
代码重复更加难以修改
重复代码中的bug会在系统中出现多次,需要修改多次才行。
3. 如何使用本原则:
- 提取方法
- 提取父类
具体细节可参考《重构 改善既有代码的设计》
4. 本节内容总结:
重复代码是“万恶之源”,是最大的软件质量问题,要切记不要在系统中出现重复代码。
四. 保持代码单元的接口简单
1. 原则:
- 限制每个代码单元的参数不能超过4个。
- 你应该将多个参数提取成对象。
- 该原则能提高可维护性的原因在于,较少的参数可以让代码单元更容易理解和重用。
2. 动机:
保持较少的接口参数并引入合适的参数对象,能够带来很多的好处。接口参数较少的方法能够保持简单的上下文,因此更容易被人理解。除此以外,由于它们并不过于依赖来自外部的输入,所以也更易于重用和修改。
-
短接口更易于理解和重用
随着代码库规模的增长,核心类会逐渐成为其他代码所依赖的API。为了避免代码量的迅速膨胀以及开发速度下降,核心类必须有一个清晰、短的接口定义。 -
短接口的方法更易于修改
过长的接口不仅让方法变得难以理解,许多情况下还表示它承担着多重的职责。从这个角度上讲,接口长短与代码单元大小和复杂度是有关系的,即接口过长的方法更难以修改。
3. 如何使用本原则:
-
改进数据模型
过长的接口本身不是问题,而是实际问题的一个表现--意味着代码中可能存在着设计不合理的数据模型,或者随意的改动。可以将接口长短视为一种代码坏味道,通过它来了解是否需要改进你的数据模型。 - 使用方法对象替换方法
4. 本节内容总结:
根据实践中的经验,将参数个数的上限设为4个是比较合理的。5个参数的方法已经开始变得难以理解,并且担负过多的职责。
五. 编写简洁的代码
1. 原则:
- 编写简洁的代码
- 你不应该在完成开发工作后留下代码坏味道。
2. 动机:
简洁的代码是可维护的代码。
3. 如何使用本原则
SIG从长期的咨询经验中总结出如下7条规则来避免产生最不利于可维护性的代码坏味道:
-
不要编写单元级别的代码坏味道
此处的单元基本指的是函数(方法)级别,要遵守这条规则,需要及时重构“坏味道”代码。 -
不要编写不好的注释
注释有些时候被看作是代码不好的表现。从实际经验中可以确认,行内注释通常意味着工程方案不够优雅。
最好的选择就是使用好的命名使得代码可以自注释。 -
不要注释代码
不要提交被注释掉的代码,版本控制系统会保留旧代码的记录,你可以安全地删除它们。
从原始开发人员的角度,FIXME注释及相关代码是能够理解的,但是对于新的开发人员却是一种干扰。 -
不要保留废弃代码
废弃代码指的是根本不会被执行或者输出被“废弃”的代码。
废弃代码的几种形式:
a. 注释掉的代码
b. 方法中无法到达的代码
c. 无用的私有方法
d. 注释中的代码 -
不要使用过长的标识符名称
通常要避免使用过长的标识符名称,团队应该有一个正式的命名规范。 -
不要使用魔术常量
魔术常量指的是在代码中没有被清晰定义的数字或者字符串。 -
不要使用未正确处理的异常
3个正确处理异常的原则:
a. 捕获一切异常
记录下系统的失败行为,是为了了解它们产生的原因并加以改进。空的catch代码块也能够编译通过,但这是一个不好的实践行为,它没有提供任何与异常上下文有关的信息。
b. 捕获特定异常
为了可以追踪某些特定事件的异常,应该捕获特定的异常。不应该直接捕获Throwable、Exception、RuntimeException异常。
c. 在展示给终端用户之前
先将特定的异常信息转换成通用的信息。用户不应该被具体的异常信息所“打扰”,因为这会让他们感到困惑,而且也会带来安全隐患(例如提供了有关系统内部工作原理的过多信息)。
4. 本节内容总结:
实际工作中会使用更多的编码规范和质量检查规范,这7个规范仅仅是SIG团队认为对编写可维护代码最重要的几条,应该严格遵守。
网友评论