坏味道
缺乏业务含义的命名
错误命名
- 宽泛的命名
- 用技术术语命名
命名遵循的原则
- 描述意图,而非细节
- 面向接口编程,接口是稳定的,实现是易变的
- 命名中出现技术名词,往往是它缺少了一个模型
- 使用业务语言
乱用英语
英语使用不当
- 违反语法规则
- 不准确的英语词汇
- 英语单词拼写错误
解决方法
- 制定代码规范
- 建立团队词汇表
- 经常性进行代码评审
重复代码
重复的代码
- 复制粘贴的代码
- 结构重复的代码
- if 和 else 代码块中的语句高度类似
消灭重复代码的原则
- 每一处知识都必须有单一、明确、权威地表述
长函数
长函数的产生
- 以性能为由
- 平铺直叙写代码
- 一次增加一点点代码
消灭长函数的原则
- 定义好函数长度的标准
- 做好“分离关注点”
重构手法
- 提取函数
大类
产生大类的原因
- 职责不单一
- 字段未分组
软件设计的原则
- 单一职责原则
极致的追求
- 每个类不超过2个字段
长参数
消除长参数
参数数量多导致的长参数
变化频率相同,则封装成一个类;
变化频率不同:静态不变的,可以成为软件结构的一部分;多个变化频率的,可以封装成几个类。标记参数导致的长参数
根据标记参数,将函数拆分成多个函数。
重构的手法
- 将参数列表封装成对象
- 移除标记参数
滥用控制语句
呈现形态
- 嵌套的代码
- else语句
- 重复的switch
- 循环语句
编程原则
- 函数至多有一层缩进
- 不要使用else关键字
重构的手法
- 以卫语句取代嵌套的条件表达式
- 多态取代条件表达式
缺乏封装
呈现形态
- 火车残骸/过长的消息链
- 基本类型偏执
编程规则
- 遵循迪米特法则
- 封装所有的基本类型和字符串
- 使用一流的集合
重构的手法
- 隐藏委托关系
- 以对象取代基本类型
可变的数据
呈现形态
- 暴露的细节
- 可变的数据
- 全局数据
编程规则
- 限制变化
- 尽可能编写不变类
- 区分类的性质,实体对象要限制数据变化,而值对象就要设计成不变类
重构的手法
- 移除设置函数
变量的声明与赋值分离
编程规则
- 变量要一次性完成初始化
应对策略
- 在声明前面加上final,用不变性的限制约束代码
- 用声明式的方式进行集合的初始化
依赖混乱
呈现形态
- 缺少防腐层,业务与外部接口耦合
- 业务代码中出现具体实现类
应对策略
- 引入防腐层,将业务与内部接口隔离
- 引入模型,将业务与具体实现隔离
编程规则
- 高层模块不应依赖于低层模块,二者应依赖于抽象。抽象不应依赖于细节,细节应依赖于抽象。
不一致的代码
呈现形态
- 命名中的不一致
- 方案中的不一致
- 代码中的不一致
应对策略
- 团队统一编码方案
- 提取函数,将不同层次的内容放入不同函数中
落后的代码风格
具体案例
- Java8引入的Optional
- 函数式编程
核心要点
- 引入Optional可以减少由于程序员的忽略而引发对空对象的问题
- 懂得最基本的几个操作:map、filter和reduce,就可以把大部分集合操作转成列表转换
编程规则
- 声明式编程
- 写短小的函数,不要在lambda中写过多代码
总结
- 好的命名,是体现业务含义的命名
- 编写符合英语语法规则的代码
- 不要重复自己,不要复制粘贴
- 把函数写短,越短越好
- 把类写小,越小越好
- 减小参数列表,越小越好
- 循环和选择语句,可能都是坏味道
- 构建模型,封装散落的代码
- 限制可变的数据
- 一次性完成变量的初始化
- 代码应该向着稳定的方向依赖
- 保持代码在各个层面上的一致性
- 不断学习“新”的代码风格,不断改善自己的代码