第一章: 重构第一个案例
1. 重构第一步:建立一组可靠的测试环境
2. 提取出函数: 选中区域,点击Extract method(可修改参数名,函数名,扔出异常,加入comment,),运行测试判断是否正确. 阅读代码时,我经常重构.这样随着对程序的理解逐渐加深,也会不断把这些理解嵌入代码中.
3. 判断子函数的参数,将参数只属于特定类的子函数复制到对应类,编译并测试
4. 移出临时变量,编译并测试
5. 对于循环增加的临时变量,extract method并将循环移入.重构时不必担心性能,优化时才需要担心.优化时可通过插入stopwatch确定性能点,从而优化
6. 对于预测变化时的重构,尽量减少受到影响的范围
7. 对于switch语句,使用state pattern重构,是为了需求变化时更加容易
第二章: 重构原则
修改功能和重构,无论何时都应该清楚自己戴的是哪一顶帽子.
重构之前:代码必须在大部分情况下能正常运作
何时重构
添加功能,修改错误
何时不该重构
项目进入最后期限
性能
程序设计时,不需要关注性能,当编写完成后,使用测试软件找到hot pot,然后针对该区域进行优化
重构的难题
数据库
通过在对象模型和数据库模型之间插入一个隔离层
修改接口
如果该接口使用者无法控制,在接口改变时,保留原接口,设为depreciated。旧接口调用新接口实现。
第三章: 代码的坏味道
重复代码:
同一类中的两个函数:extract method
同一类的两个子类:extract method, pullup method, form template method
做同一件事的两个不同算法:使用一个清晰的取代其他,substitute algorithm
两个不相干的类:将共有代码extract class ,另一个类调用,也需要从语义上判断该函数是否属于一个类
large function:
让function容易理解的关键是起一个好名字
每当需要以注释解释函数时,就将要说明的东西写入一个函数并以函数的用途来命名它,哪怕替换后的函数比原来还长,只要函数名上看出其用途,也应该毫不犹豫地这样做.
方法:
99%的场合:Extract method
太多的参数和临时变量: replace temp with query和introduce parameter object
临时变量被多次复制:split temporary variable
该提炼哪些:
寻找注释. 就算只有一行代码,如果其需要注释,也应该将其提出到一个函数
条件: decompose conditional
循环:将循环和其内的代码提炼到一独立的函数
large clas
方法:
先找到彼此相关的变量(有相同前缀或后缀);提炼该变量相关的代码:extract class, move field, move method
太多参数
方法:
参数由函数计算得到:replace parameter with method
参数所在对象存在:preserve whole object
参数的对象不存在:introduce parameter object
类的职责
多种原因的变化要修改一个类:extract class
一个变化修改多个类: move field,move method
依恋情节:两个类耦合
调用某一类过多的取值函数:move method
函数的部分:先extract method,再move method
一个函数用到多个类:先extract method, 将该函数放到用到数据最多的类
Data clumps:数据泥团
删除数据的一项,其他数据不再有意义
方法:
将这些字段 Extract class;
函数参数 introduce parameter object
不必在意只用上字段的一部分字段。
Switch statements
单一函数中:replace parameter with explicit method
选择条件之一是null: introduce null object
大量使用:1)extract method将switch提取到独立函数;2)move method到多个类;3)replace type code with subclass, replace type code with state/strategy
Parallel Inheritance Hierarchies 平行继承体系
每当为一个类增加子类,需要为另一类应增加一个子类
方法:
让继承体系的一个子类引用另一个体系的实例;move method ,move field;就可以消除引用端的继承体系
Speculative Generality
如果函数或类的唯一用户是测试用例
类:inline class 或者collapse hierarchy
函数的某些参数:remove parameter
函数名过于抽象:rename method
Message Chains
一长串的getThis()
观察该函数具体的作用,将该函数放入消息链的正确位置
Middle Man:过度委托
某个类有一半的函数都委托给其他类
方法:
remove middle man;
少数几个函数:inlineMethod
Incomplete Library Class:不完美的库类
修改一两个函数:Introduce Forign Method
增加一大堆行为:Introduce Local Extension
Data Class
除了get set 什么也不干
找到调用处,move method; 对不提供修改的字段 remove set method;后期可用hide method隐藏get和set
Comments
说明参数规格: introduce assertion
合理的注释:将来的打算,为什么要这么做
第四章构筑测试体系
写程序最花时间的是调试错误,找出错误比较费时,改正错误是很快的
作用:
描写该功能如何使用
集中于接口而非实现