01 缺乏业务含义的命名
- 不精准的命名
- 用技术术语命名
描述意图,而非细节;
面向接口编程;
使用业务语言。
02 乱用英语
- 违反语法规则的命名
- 不准确的英语词汇
- 英语单词的拼写错误
- 使用拼音进行命名
- 使用不恰当的单词简写(比如,多个单词的首字母,或者写单词其中的一部分)。
类名要用名词,函数名要用动词或动宾短语;
要建立团队的词汇表;
要经常进行代码评审;
编写符合英语语法规则的代码。
03 重复代码
- 复制粘贴的代码
- 结构重复的代码
- if 和 else 代码块中的语句高度类似
在软件开发里,有一个重要的原则叫做 Don't Repeat Yourself(不要重复自己,简称 DRY);
在一个系统中,每一处知识都必须有单一、明确、权威地表述。
04 长函数
- 超过20行
- 平铺直叙写代码
- 一次增加一点点代码
分离关注点;
把函数写短,越短越好。
05 大类
- 职责不单一
- 字段未分组
应对大类的解决方案,主要是将大类拆分成小类,本质上在做的是设计工作;
模块拆分,本质上是帮助人们降低理解成本的一种方式;
把类写小,越小越好。
06 长参数列表
应对长参数列表主要的方式就是减少参数的数量,一种最直接的方式就是将参数列表封装成一个类。但并不是说所有的情况都能封装成类来解决,我们还要分析是否所有的参数都有相同的变动频率。
-
变化频率相同,则封装成一个类
-
变化频率不同的话:
-
静态不变的,可以成为软件结构的一部分
-
多个变化频率的,可以封装成几个类
除此之外,参数列表中经常会出现标记参数,这是参数列表变长的另一个重要原因。对于这种标记参数,一种解决方案就是根据这些标记参数,将函数拆分成多个函数。
减小参数列表,越小越好。
07 滥用控制语句
- 嵌套的代码
- else 语句
- 重复的 switch
- 循环语句
函数至多有一层缩进;
不要使用else关键字;
多态取代条件表达式;
循环和选择语句,可能都是坏味道。
08 缺乏封装
- 过长的消息链,连续的函数调用,把实现细节暴露了出去,缺乏应有的封装
- 基本类型偏执,用各种基本类型处传递,这种情况下通常是缺少了一个模型
遵循迪米特法则;
封装所有的基本类型和字符串;
隐藏委托关系;构建模型,封装散落的代码。
09 可变的数据
- 暴露细节
- 可变的数据
- 全局数据
限制变化;
尽可能编写不变类;
实体对象要限制数据变化,而值对象就要设计成不变类;移除设值函数(Remove Setting Method)。
10 变量声明与赋值分离
- 变量的声明和赋值是分离的
在能够使用 final 的地方尽量使用 final,限制变量的赋值;
用声明式的方式进行集合的初始化;
一次性完成变量的初始化。
11 依赖混乱
- 缺少防腐层,请求对象传导到业务代码中,造成了业务与外部接口的耦合
- 业务代码中出现具体的实现类,实际上是违反了依赖倒置原则
引入防腐层,将业务与内部接口隔离;
引入模型,将业务与具体实现隔离。
12 不一致的代码
- 命名中的不一致
- 方案中的不一致,一方面是由于代码长期演化造成的,另一方面是项目中存在完成同样功能的程序库;
- 代码中的不一致,把不同层次的代码写在了一起,最典型的就是把业务层面的代码和实现细节的代码混在一起。
保持代码在各个层面上的一致性。
13 落后的代码风格
- 引入Optional,减少由于程序员的忽略而引发对空对象的问题,类似 Kotlin 的可空类型
- 函数式编程,map、filter 和 reduce把大部分的集合操作转换成列表转换
不断学习“新”的代码风格,不断改善自己的代码。