函数重构规则
一.Extract Method(提取函数) — 将大函数按模块拆分成几个小函数
二.Inline Method(内联函数) — 将微不足道的小函数进行整合
- 将过度封装的函数在放回去,或者将那些没有必要封装的函数放回去。
三.Replace Temp with Query(以查询取代临时变量) — 将一些临时变量使用函数取代
- 将那些有着复杂表达式赋值并且多次使用的临时变量使用查询函数取代,也就是说该临时变量的值是通过函数的返回值来获取的。这样一来在实现类似功能的函数时,这些复杂的临时变量就可以进行复用,从而减少代码的重复率。
- 如果临时变量所代表的表达式多次使用,我们就可以对上述函数在此使用Replace Temp with Query规则进行重构。
四.引入解释性变量:将复杂的表达式拆分成多个变量
- 表达式过长,引入入解释性变量。顾名思义,我们引入的变量是为了解释该表达式中的一部分的功能的,目的在于让该表达式具有更好的可读性。
五.分解临时变量
- 也就是一个临时变量不能赋上不同意义的值
六.移除对参数的赋值
- 也就是说你在函数的作用域中不要对函数的参数进行赋值,也不能对参数进行修改
类重构规则
一.方法迁移
- 就是当类中的方法不适合放在当前类中时,就应该为该方法寻找合适下家。如果一个类有太多行为,或者与另一个类有太多合作而形成高度耦合。此时就应该将该方法搬移到其高度依赖的类中,再进行函数重构
二.搬移字段
- 当在一个类中的某一个字段,被另一个类的对象频繁使用时,我们就应该考虑将这个字段的位置进行更改了
三.提炼类
四.隐藏委托关系
- 这两个类互为依赖关系,Department中有People,该People对应的就是经理人,使用Hide Delegate进行重构的方式是比较简单的,就是在People中封装一个方法,在方法中返回经理的对象即可,这样就隐藏掉了委托关系。
public People getManager() {
return this.department.manager .
}
五.引入外加函数
- 继承
数据重构规则
一.自封装字段
- get set 方法
二.以对象取代数据值
- 实体类
三.以字典常量取代魔法值
四.封装集合
class Lender {
private var name: String
private var lendBooks: Array<LibraryBook> = []
init(name: String) {
self.name = name
}
func getName() -> String {
return self.name
}
func setLendBooks(books: Array<LibraryBook>) {
self.lendBooks = books
}
func getLendBooks() -> Array<LibraryBook> {
return self.lendBooks
}
}
条件表达式重构规则
一.分解条件表达式
- 一般拆分的规则为:经if后的复杂条件表达式进行提取,将其封装成函数。如果if与else语句块中的内容比较复杂,那么就将其提取,也封装成独立的函数,然后在相应的地方进行替换。
二.移除控制标记
- 使用标记变量最直观的感受就是不易维护,不易理解。因为在需求变更或者迭代中,你还得维护这标记变量。
- 标记变量一般是可以使用其他语句进行替换的,可以使用break、return、continue等等
三.以卫语句取代嵌套的条件
- 尽量不要将if-else进行嵌套,因为嵌套的if-else确实不好理解
- 我们可以将if后的条件进行翻转,根据具体需求再引入return、break、continue等卫语句。
四.以多态取代条件表达式
- 如果在你的条件表达式中条件是对象的类型,也就是根据对象的不同类型然后做不同的事情。在这种情况下使用多态在合适不过了。
- 状态模式
- 如果想使用多态,引入其他类是必不可少的,而且每个类中也必须有相应的对应关系。“以多态取代条件表达式”的做法的本质是将不同状态的业务逻辑的处理的代码移到相应的类中
继承关系重构规则
一.Pull Up Field (字段上移) & Pull Down Field (字段下移)
- 字段上移 : 也就是说将子类中相同的字段移到父类中。在该实例中就是讲var a = 0 移到父类中。
- 字段下移 :父类中有某些字段,但是这些字段只有在少数子类中使用到,在这种情况下我们需要将这个字段移到相应的子类
- 函数上移,函数下移
二.Extract Subclass (提炼子类)
- 当类中的某些方法只有在特定的类的实例中才会使用到,此时我们就需要提炼出一个子类,将该方法放到相应的子类中。这样一来我们的每个类的职责更为单一,这也就是我们常说的“单一职责”。
- 例如:vip方法 相对于 普通消费者类 ,将VIP用户作为普通用户的子类,然后将只有VIP用户才调用的方法放到我们的VIP子类中。这样一来层次更加明确,每个类的职责更为单一
三.Form Template Method (构造模板函数)
- 两个兄弟类中的两个函数中的实现步骤大致一直,但是具体细节不同。在这种情况下,我们就可以将大体的步骤提取成模板,放到父类中,而具体细节由各自的子类来实现
四.以委托取代继承(Replace Inheritance with Delegation)
- 发现子类只使用了父类的部分方法,而且没有继承或者部分继承了父类的数据。在这种情况下我们就可以将这种继承关系修改成委托的关系。具体做法就是修改这种继承关系,在原有子类中添加父类的对象字段,在子类中创建相应的方法,在方法中使用委托对象来调用原始父类中相应的方法
- 在子类中去掉继承关系,生成代理对象。
五.以继承取代委托(Replace Delegation with Inheritance)
- 与上相反
重构完整案例
- 重构时,单元测试是少不了的
步骤
- “Extract Method”(提炼函数):原则对该函数进行简化和拆分。将statement()中可以独立出来的模块进行提取
- 重构2:将相应的方法移到相应的类中 :判断该函数比较依赖那个类?
- 重构3:使用“以查询取代临时变量”再次对statement()函数进行重构 :
重构前:
问题:举个例子,如果我们要将结果以HTML的形式进行组织的话,我们需要将上面的代码进行复制,然后修改result变量的文本组织方式即可。但是这样的话,其中的好多临时变量也需要被复制一份,这是完全相同的,这样就容易产生重复的代码。
重构后:
- 继续对函数进行移动 : 对类的依赖性:因为这两个函数都只用到了Rental类中的daysRented属性,而多次用到了Movie中的内容。因此我们需要将这两个函数中的内容移到Movie类中更为合适。
- 使用多态取代条件表达式 : 具体实现方式是将不通的价格计算方式提取到我们新创建的价格类中,每种电影都有自己价格类,而这些价格类都实现同一个接口,这样一来在Movie中就可以使用多态来获取价格了