重构(ruby版)摘要
1 代码里的坏味道
0 重复代码
1 类太大
2 参数太多
3 发散型变化
比如:“嗯,每次得到一个新数据我都要修改这三个方法。“
4 霰弹型修改(平行继承体系)
每次对某类修改时,都要修改其他类
5 特性依赖
某个方法似乎更对另一个类(而不是本身所在的)类更感兴趣
6 数据泥团
有些数据总是同进同出
最基本原则就是把一起变化的东西放在一起
7 基本类型偏执
8 case 语句
试着使用多态
9 废弃代码,过度设计
10 临时字段
数据库里有些字段很少使用,某个实例变量只在特殊情况下使用
11 被拒绝的遗产
重构之前必须有测试
红灯/绿灯/重构
当你收到一份bug报告时,首先编写单元测试来暴露bug
编写不完整的测试也比没有测试好
思考可能会出错的边界条件,并集中测试它们
组织方法
1 使用查询替换临时变量
2 使用链式调用
3 引入解释性变量
4 分解临时变量
temp = 2 * (@height + @width)
puts temp
temp = @height + @width
puts temp
perimeter = 2 * (@height + @width)
puts perimeter
area = @height + @width
puts area
5 移除对参数的赋值
6 提炼环绕方法
有两个代码几乎一样的方法。唯一的区别是方法中间的一小段代码
7 引入类标注(动态方法定义)
8 引入命名参数
方法调用的参数无法轻易地从方法的名字中推断出来
1 命名全部参数
2 只命名可选参数
9 动态
1 使用动态方法定义替换动态接收器
2 隔离动态接收器
3 把计算从运行时移到解析时
在对象间移动特性
1 一个方法正在使用更多另一个类的特性而不是自身所在的类的特性
2 有一个类做了应该由两个类负担的工作
3 隐藏委托
使用对象的关键之一,就是封装。封装意味着减少对象系统对系统的其他部分所需的理解。这样当事情发生变化时,需要知道改变的东西就变少了
4 值对象和引用对象
5 使用符号常数代替魔法数字
6 封装集合
让它返回一份集合的拷贝,并提供添加和删除方法
一个类中常常会包含一个实例的集合。这个集合可能是个数组,也可能是个hash。集合应该采用一种和其他类型的数据略微不同的策略。读取器不应该返回集合对象本身,因为这样客户就能在拥有集合的类不知晓的情况下操作集合的内容
7 使用多态,模块扩展, 策略模式替换类型码
8 惰性初始化属性
简化方法调用
如果一个方法能经由其他途径获得作为参数传递进来的值,那么它就应该采用那种方法(而不是接受参数),过长的参数列表太难理解,我们应该尽可能缩减它的长度
1 重命名方法
记住你的代码是以人为本的,机器只是第二位
2 分离查询方法和修改方法(并发问题)
3 参数化方法 使用显式方法替换参数
4 保留完整对象
从一个方法中获取多个值,然后将它们作为参数传递给某个方法调用
5 引入参数对象
有一组总是一起出现的参数
6 移除设值方法
某个方法应该在创建的时候设置,然后不再变化
7 隐藏方法
8 使用工厂方法替换构造函数
工厂模式,抽象工厂模式
9 使用异常替换错误码
将正确的处理和对错误的处理分离开,使程序更容易理解
不要过度使用异常,有时候普通的检测就可以了
10 引入网关 引入表达式构造器
网关:用一种简化的方式来和某个外部系统或者资源的复杂api进行交互
表达式构造器:API通常在设计的时候都会为对象提供一组独立的方法。在理想状态下,这些方法都可以单独理解。这与一套流畅的和围绕一整个表达式的可读性设计的接口是背道而驰的。流畅的接口通常会让它的方法无法单独理解
表达式构造器在常规的api上额外提供了一层流畅的接口。它只有一个任务,就是提供流畅的接口,原来的对象可以保留那些能够让人单独理解的方法接口
http.post(:first_name, :last_name).to("www.google.com")
代理,适配器模式
其他:
1 使用模块替换抽象父类
一些父类永远不会实例化
2 构造模板方法
子类中有两个顺序相同,步骤类似的方法,但是步骤还是有区别的
def billable_amount
base = @units * @rate * 0.5
tax = base * Site::TAX_RATE
base + tax
end
def billable_amount
base = @units * @rate
tax = base * Site::TAX_RATE * 0.2
base + tax
end
def billable_amount
base_amount() + tax_amount()
end
3 使用守卫字句替换嵌套条件语句