Refactor
最近在写代码的时候,发现之前写的代码各种ifelse嵌套,类函数过多,函数体过长,函数变量命名不清晰,遂决定重构之,于此同时看了重构这本书,收益颇多!
整本书从data,method,class,module,system的这几个层面去讲如何重构,重构的基本原则以及注意事项依次来编写高质量,可维护,可扩展的代码,主要读书笔记如下:
何为重构
在不改变软件可观察行为的前提之下,使用一系列重构方法,对软件内部结构进行调整,提高代码的可理解性,降低扩展成本
重构的原动力
当代码的设计无法帮助我们轻松添加新的特性(无法扩展),因此需要重构程序使程序满足以下条件:
- 容易阅读
- 所有逻辑都只在唯一地点指定
- 新的改动不会危及现有行为
- 尽可能简单表达条件逻辑
在重构的过程中我们会发现Middle Tier(中间层)会被经常使用,使用中间层的好处主要体现在以下几个方面:
- 允许逻辑共享,单个函数在多个地方进行调用
- 分开解释意图和实现(类和函数的名字可以表达自己的意图)
- 隔离变化
- 封装条件逻辑
何时重构
如果每次对代码的更改都会做重复的事情,那么就必须重构
修改接口
对于对象而言可以分开修改软件模块的实现和接口,对于模块的实现可以任意进行修改而不影响他人,但是不能随意更改接口
重构方法
简化条件表达式
当函数中如果出现多层if,else嵌套,一长串的switch语句,程序的可读性是非常差的,我们可以用以下几点重构之:
- 从if,then,else中分解条件表达式(提炼独立的函数),突出条件逻辑,更清楚的表明每个分支的作用(增加可读性,见其名知其意)
- 合并条件表达式(如果一系列条件测试得到相同的结果,那么将所有提交测试提炼成一个独立的函数)(减少条件表达式)
- 合并重复的条件片段条件表达式中每个分支都有相同的代码,在相同代码不随条件表达式的改变而改变的前提下,将相同代码移至条件表达式外(减少冗余代码)
- 以卫语句取代嵌套条件表达式使用卫语句表现所有的特殊情况,卫语句要么从函数中返回,要么抛出一个异常(针对单一出口的观点,最好在不影响代码可读性的情况下才使用)
- 以多态取代条件表达式:因为多态的出现,导致基于类型的switch语句和基于类型名称的if-else语句很少出现,对于多个if-else或者switch只用一个对基类的判断即可,那么在运行时中自动会调用相应类的方法(不必询问对象是什么,只管调用对应的的行为)(减少switch 的case语句)
- 引入null对象P260
- 实现一个空对象,覆盖基类的isNull方法()返回true,
- 在所有对对象判断是否为空的地方,直接调用isNull方法
- 对空对象实现默认的空行为,这样可以不用判断是否为空,直接调用相应地函数(如果对象为空对象,那么会自动调用空行为)
- 空对象可能会返回空对象
- 引入断言增加代码可读性并且帮助调试
对于简化条件表达式,我认为代码大全讲了更多地简化技巧
Organizing Data
这一章更多地是侧重于不同情况下组织数据的一些技巧
- 将引用对象改为值对象值对象主要是不可变性,所以当确定一个引用对象不可变的时候,去掉setter方法,私有化成员变量,重写hashCode(集合类通过hashCode来快速定位对象的位置)和equals(来判断两者的地址是否相同)方法
- 用类替代类型码,人的血型可以用类来进行封装p220(类型码不影响主类)
- 用子类替代类型码,(类型码影响类的变化,可以用子类进行多态化处理,类的静态实例化函数通过类型码进行动态创建子类,子类实现抽象方法(getType)来返回正确地子类类型
- 用state/strategy取代类型码实质跟多态取代子类相似
Moving Features Between Objects
核心就是如何抽象公共部分,较少冗余代码
- move method:将公共方法提取至基类
- move field
- extra class(提炼类)
- inline class (类内联化,将一个类的所有method和field都移入新的类)
- hide delegate(隐藏委托关系) 在服务类建立客户所需的所有函数,用以隐藏委托关系Person Department Manager
- remove middle man (移除中间人)(当客户端每次使用受托类的新特性的时候,都必须在服务端增加委托函数,所以新特性比较多得时候,客户端可以直接调用受托类
- introduce foreign method 在客户类中建立一个函数,以第一参数形式传入一个服务类实例
简化函数调用
最重要的还是给函数和参数取一个好名字把
- rename method
- add parameter
- remove parameter
- 将查询函数和修改函数分离(任何有返回值的函数都不应该有side effect,不要使一个函数同时包含查询和修改)
- preserve whole object(保持对象的完整性,不用传递多个参数,可以传递一个对象,保持代码的整洁性)
- 以函数取代参数
- 引入参数对象(都是为了缩短参数列长度)
- remove setting method (某个字段设置为final的,不可改变)
- hide method(当一个函数如果不被其他类引用时)
- 以工厂函数取代构造函数
- 封装向下转型
- 以异常取代错误代码
- 以测试取代异常
处理继承关系
这章侧重于如何处理子类和父类的field和method的关系
- pull up field 字段上移
- pull up method 函数上移
- 构造函数本体上移
- 函数下移
- 字段下移
- extract subclass 提炼子类
- 提炼超类
感想
是个人都能写出让机器看懂的代码,但并不是所有人能写出其他人能看懂的代码,所以培养良好的代码风格是一件非常主要的事情,今后可能会遵守下面三条规则去规范自己培养良好的编码习惯
- 注意函数命名,代码风格(各个语言参看对应系统库的代码风格),尽量写clean code
- 在写代码之前,想好代码为什么这么写,这样写有什么好处,是否易于扩展,几个月之后自己是否还看得懂,别人是否看得懂
- 找人code review,牛人对代码的认知以及整个项目的理解是跟自己不一样的,多问为什么这么设计以及这样设计的好处并思考这样设计的优缺点