前言
在日常编码的过程中,大家要时刻警惕代码中存在的坏味道,要经常对原有的代码做重构,下面会列举一些代码中普遍存在的坏味道。
1、缺少业务含义的命名
1、 命名过于宽泛
这个在我的项目中其实存在很多,大家经常的命名大多都会以data、info、process、handle、build,manage等开头或者结尾,但其实这些命名过于宽泛,大家也不愿意花时间去想一个恰当的名字。
2、用技术术语命名
这种在我的项目中随处可见,就比如一个变量叫做bookList,原因就是它对应的类型是List,还有上面的Map,这种是基于实现细节命名的,编程的时候有一个重要原则就是面向接口而非实现,原因是接口是稳定的,而实现是易变的,就比如上面entityMap,我如果后续返回的改成了List,那它的名字也要相应做修改。
3、命名遵循的规范
- 描述意图,而非细节
- 面向接口编程,因为接口是稳定的,而实现是易变的
- 别用技术术语命名
- 好的命名,应该是体现业务含义的命名
2、乱用英文
1、命名规则
- 类名应该是一个名词,表示一个对象
- 方法名是一个动词,或者是动宾短语,表示一个动作
- 参数,属性是名词
2、英语使用不当
- 注意英语拼写错误
- 不准确的英语词汇
- 违反语法规则
3、解决办法
- 执行代码规范
- 建立团队词汇表
- 经常性进行代码review
3、重复代码
1、赋值粘贴的代码
现在在很多时候写代码仍然避免不了复制粘贴,基本的做法就是把代码复制过来,改动几个地方,然后泡一下就可以了。但是通常情况下,只要这些复制代码其中有一点逻辑要修改,就意味着所有复制粘贴的地方都要修改 ,所以一般可能只是一个简单的需求,可以改动的地方特别多。
2、结构重复的代码
有些代码虽然看起来命名不一样,但其实结构上执行的流程是一指定的,这个时候就需要把这个结构提取出来,这样后续有一些通用的调整就不用四处修改了。
3、if,else中的语句高度相似
如下面这段代码,只有最后一个参数不同,这也是一种代码冲突,下面优化的代码就是把变化的点提取出来了。
if (user.isEditor()) {
service.editChapter(chapterId, title, content, true);
} else {
service.editChapter(chapterId, title, content, false);
}
boolean approved = user.isEditor();
service.editChapter(chapterId, title, content, approved);
4、解决办法
不要重复自己,不要复制粘贴
4、长函数
1、长函数的产生
- 平铺直叙写代码,把不同层次不同逻辑的代码写到同一个方法中
- 一次增加一点点代码,日积月累就变成了长函数
2、消灭长函数的方法
- 定义好函数长度的标准
- 做好“关注点”分离,让每个函数的粒度尽量小,尽量只做一件事
- 坚守"童子军军规",让营地比你来时更干净
3、解决办法
提取函数
5、大类
1、产生大类的原因
- 指责不单一
- 字段未分组
2、解决办法
遵循软件设计的原则,单一指责原则,尽量把大类的职责拆分的更细,拆分成不同职责的小类。
6、长参数列表(方法中的参数列表特别长)
1、消灭长参数
- 参数数量多导致的长函数,把变化频率相同的封装成一个类,变化频率不同的多个变化频率的可以封装成多个类,静态不变的,可以成为软件结构的一部分
- 标记参数导致的长函数,可以根据标记参数把函数拆分成多个函数
2、解决办法
- 将参数列表封装成对象
- 移除标记参数
7、滥用控制语句
1、坏味道呈现形式
- 嵌套的代码
- else语句
- 重复的switch
- 循环语句
2、编程规则
- 函数至多有一层缩进
- 不使用else关键字
- 使用卫语句代替嵌套的条件表达式
- 使用多态代替switch
8、缺乏封装
1、坏味道呈现方式
- 连续的函数调用
- 基本类型偏执
2、编程规则
- 封装所有的基本类型和字符串
- 隐藏委托关系
- 使用对象代替基本类型
- 遵循迪米特法则
每个单元对其它单元只拥有有限的知识,而且这些单元是与当前单元有紧密联系的;
每个单元只能与其朋友交谈,不与陌生人交谈;
只与自己最直接的朋友交谈。
9、可变的数据
1、坏味道呈现方式
- 暴露的细节(set)
- 可变的数据
- 全局数据
2、编程规则
- 限制变化
- 尽可能编写不变类
- 区分类的性质,实体对象要限制数据变化,而值对象要设计成不变类
- 移除设置函数。
10、变量的声明与赋值分离
1、坏味道呈现方式
变量的声明和赋值间隔很远,且赋值方式较为复杂。
2、编程规则
- 在声明前面加final,用不变性约束代码,能使用final尽量使用final,限制变量的赋值
- 变量最好一次性完成初始化
- 使用声明式的方式进行集合的初始化
11、依赖混乱
1、坏代码呈现方式
- 缺少防腐层,业务与外部接口耦合,如平时我们接受请求的request对象,他可能在对应的service也需要依赖这个对象,那么他属于哪一层,这就有些混乱了,所以可以引入防腐层,在service中添加对应的BO
- 业务代码中出现具体实现类
2、编码规则
- 引入防腐层,将业务与外部接口隔离
- 引入模型,将业务与具体实现隔离
- 高层模块不应该依赖于底层模块,二者应该依赖于抽象
- 抽象不应该依赖于细节,细节应该依赖于抽象
12、不一致的代码
1、坏味道呈现方式
- 命名中的不一致
- 方案中的不一致
- 代码中的不一致
2、编码规则
- 团队统一编码方案
- 提取函数,将不同层次的内容放到不同函数中,保持代码在各个层面上的一致性。
13、落后的代码风格
1、具体案例
- 函数时编程
- java8引入的Optional
2、 编程规则
- 声明式编程
- 写短小的函数,不要在lambda中写过多代码。
- 不断学习新的编码风格,不断改善自己的代码
14、代码评审
- 代码评审的本质是沟通反馈,尽可能多做代码评审,尽可能多暴露问题出来。
- 代码评审的角度应该从实现方案的正确性,算法的正确性,代码的坏味道
- 代码评审的极致是结对编程
15、新需求破坏原有代码
1、 坏代码呈现方式
- 增加新接口
- 改动实体字段
2、 编程规则
- 对外提供的接口越少越好
- 仔细分析实体扮演的角色,防止后续大量的改动
- 谨慎的对待接口和实体的变动。