重构概述
为什么要重构? 保持代码质量持续处于一个可控状态,不至于腐化到无可救药的地步
重构什么?
大规模高层次的重构
内容:对代码分层、模块化、解耦、梳理类之间的交互关系、抽象复用组件
理论基础:设计思想、原则、模式
小规模低层次的重构
内容: 规范命名、注释、修正函数参数过多、消除超大类、提取重复代码等编程细节问题,主要是针对类、函数级别的重构
理论基础: 编码规范
什么时候重构? 建立持续重构意识,把重构作为开发必不可少的部分融入到开发中
如何重构?
大规模高层次的重构 难度比较大,需要有组织、有计划地进行,分阶段地小步快跑
小规模低层次的重构 影响范围小,改动耗时短,随时随地都可以去做
单元测试
什么是单元测试?
概念:代码层面的测试,用于测试“自己”编写的代码的逻辑正确性
“单元”:一般是类或函数,而不是模块或者系统
为什么要写单元测试?
能有效地发现代码中的 Bug、代码设计上的问题
写单元测试的过程本身就是代码重构的过程
单元测试是对集成测试的有力补充,能帮助我们快速熟悉代码,是 TDD 可落地执行的折中方案
如何编写单元测试?
概念:针对代码设计覆盖各种输入、异常、边界条件的测试用例,并将其翻译成代码的过程
方法:可以利用一些测试框架来简化测试代码的编写
5个正确的编写认知
1编写单元测试尽管繁琐,但并不是太耗时
2我们可以稍微放低单元测试的质量要求
3覆盖率作为衡量单元测试好坏的唯一标准是不合理的
4写单元测试一般不需要了解代码的实现逻辑
5单元测试框架无法测试多半是代码的可测试性不好
单元测试为何难落地执行?
写单元测试本身比较繁琐,技术挑战不大,很多程序员不愿意去写
国内研发比较偏向“快糙猛”,容易因为开发进度紧,导致单元测试的执行虎头蛇尾
没有建立对单元测试的正确认识,觉得可有可无,单靠督促很难执行得很好
代码的可测性
什么是代码的可测性? 针对代码编写单元测试的难易程度
依赖注入是编写可测试性代码的最有效手段
依赖注入可以通过 mock 的方法将不可控的依赖变得可控
除了 mock 方式,我们还可以利用二次封装来解决某些代码行为不可控的情况
常见的5种 Anti-Patterns
1代码中包含未决行为逻辑
2滥用可变全局变量
3滥用静态方法
4使用复杂的继承关系
5高度耦合的代码
大型重构:解耦
“解耦”为何如此重要?
保证代码松耦合、高内聚,是控制代码复杂度的有效手段
代码高内聚、松耦合,也就是意味着,代码结构清晰、分层、模块化合理、依赖关系简单、模块或类之间的耦合小,那代码整体的质量就不会差
代码是否需要“解耦”?
间接的衡量标准
改动一个模块或类的代码受影响的模块或类是否有很多
改动一个模块或者类的代码依赖的模块或者类是否需要改动
代码的可测试性是否好
直接的衡量标准 把模块与模块之间及其类与类之间的依赖关系画出来,根据依赖关系图的复杂性来判断
如何给代码“解耦”?
封装与抽象
中间层
模块化
其他设计思想与原则
单一职责原则
基于接口而非实现编程
依赖注入
多用组合少用继承
迪米特法则
设计模式
小型重构:编码规范(20条)
命名与注释
1命名的关键是能准确的达意
2借助类的信息来简化属性、函数的命名,利用函数的信息来简化函数参数的命名
3命名要可读、可搜索。不要使用生僻的、不好读的英文单词来命名
4接口有两种命名方式。一种是在接口中带前缀"I",另一种是在接口的实现类中带后缀“Impl”。抽象类的命名,我们更倾向于带有前缀“Abstract”
5注释的内容:做什么、为什么、怎么做。复杂的类和接口,我们还需要写明“如何用”
6类和函数一定要写注释,而且要写的尽可能全面详细些
代码风格
7函数代码行数不要超过一屏幕的大小,比如50行
8一行代码最好不要超过IDE的显示宽度
9善用空格分割单元块
10推荐2格缩进,节省空间。一定不要用tab键缩进
11将大括号跟上一条语句同行,可以节省代码行数,另起新的一行,接口清晰
12类中先写成员变量后写函数。成员变量之间或函数之间,先写静态成员变量或函数,后写普通变量或函数,并且按照作用域大小依次排列
编程技巧
13将复杂的逻辑提炼拆分成函数和类
14通过拆分成多个函数的方式来处理参数过多的情况
15通过将参数封装为对象来处理参数过多的情况
16函数中不要使用参数来做代码执行逻辑的控制
17移除过深的嵌套层次,方法包括:去掉多余的 if 或 else 语句,使用 continue、break、return 关键字提前退出嵌套,调整执行顺序来减少嵌套,将部分嵌套逻辑抽象成函数
18用字面常量取代魔法数
19利用解释性变量来解释复杂表达式
20项目、团队,甚至公司,一定要制定统一的编码规范,并且通过 Code Review 督促执行