代码重构
基础
- 重构的定义:对软件内部结构的一种调整,目的是在不改变”软件之可察行为“前提下,提高其可理解性,降低其修改成本。
- 重构的意义
- 优秀设计的根本是:消除重复部分!(DRY = Don’t repeat yourself)
- 重构让代码更清晰,更容易理解
- 清晰的代码可以更方便的找到bug,重构可以写出更强健的代码
- 良好的设计可以在长远时间上提高开发速度
- 什么时候重构
- 随时进行重构(在我看来,重构更是一种开发的习惯)
- 事不过三,代码重复不要超过三次(否则就要”抽“出来)
- 添加功能时候并一一重构(个人理解是,添加新功能之前,分析并重构,从而更方便添加新功能)
- 修补错误时
- code review时
- 重构与性能
- 重构确实会在短期内降低代码执行效率,但优化阶段是可以调整的,而且调整会更容易。
- 提前优化是万恶之源
- 从测试开始
- 无测试,无重构,只依赖手工测试,重构时候人会崩溃的。
- 重构的保证就是自动化测试
避免事项
- 重复的代码(这才是真正万恶之源,鄙视一切Ctrl+C/P)
- 过长函数,会导致责任不明确/难以切割/难以理解等一系列问题
- 过大类,职责不明确,垃圾滋生地
- 过长参数列(面向对象不是说说而已)
- 发散式变化,一个类会响应多种需求而被修改
- 散弹式修改(其实就是没有封装变化处,由于一个需求,多处需要被修改)
- 依赖情节(一个类对其他类过多的依赖)
- 数据泥团(如果数据有意义,就将结构数据变成对象)
- type code,使用Class替代
- switch,少用,考虑多态
- 过多平行的类,使用类继承并联起来
- 冗余类,去除它
- 夸夸其谈的未来性(Matin的文字,侯俊杰的翻译真是…出彩…)
- 临时值域,封装它
- 过度耦合的消息链,使用真正需要的函数和对象,而不要依赖于消息链
- 过度的deleate
- 过度使用其他类private值域
- 重复作用的类
- 不完美的类库,(类库老了,使用者也没办法阿)
- 纯数据类(类需要行为)
- 不纯粹的继承(拒绝父类的接口的类)
- 过多注释,注释多了,就说明代码不清楚了
重构原则
函数重构规则
- Extract Method(提取函数)-------将大函数按模块拆分成几个小的函数
- Inline Method ---- 内联函数:将微不足道的小函数进行整合
- Introduce Explaining Variable---引入解释性变量:将复杂的表达式拆分成多个变量
- Remove Assignments to Parameters----移除对参数的赋值
- Replace Method with Method Object----以函数对象取代函数:一个函数如果参数过多,可抽为
类+函数+参数
。
类重构规则
- Move Method----方法迁移:当类中的方法不适合放在当前类中时,就需要迁移。
- Move Field----搬移字段:当在一个类中的某一个字段,被另一个类的对象频繁使用时,我们就应该考虑将这个字段的位置进行更改了。
- Extract Class----提炼类:一个类如果过于复杂,做了好多的事情,违背了“单一职责”的原则,所以需要将其可以独立的模块进行拆分,当然有可能由一个类拆分出多个类。
- Introduce Foreign Method----引入外加函数:在不想或者不能修改原类的情况下,为该类添加新的方法。使用包装设计模式。
数据重构规则
对数据重构是很有必要的,因为我们的程序主要是对数据进行处理。如果你的业务逻辑非常复杂,那么对数据进行合理的处理是很有必要的。对数据的组织形式以及操作进行重构,提高了代码的可维护性以及可扩展性。
- Self Encapsulate Field (自封装字段):虽然字段对外是也隐藏的,但是还是有必要为其添加getter方法,在类的内部使用getter方法来代替self.field,该方式称为自封装字段,自己封装的字段,自己使用。
- Replace data Value with Object(以对象取代数据值)
- Change Value to Reference (将值对象改变成引用对象)
- Replace Array or Dictionary with Object(以对象取代数组或字典):数据组合起来代表一定的意义,这是最好将其定义成一个实体类。
- Duplicate Observed Data(复制“被监测数据”):即不要将业务代码和非业务代码糅合在一起。比如,不要再Integration层写业务代码。
- Change Unidirectional Association to Bidirectional(将单向关联改为双向关联):Customer与Order的关系是单向关联的,也就是说Order引用了Customer, 而Customer没有引用Order。
- Encapsulate Field(封装字段):public-->private,再通过getter/setter操作。
- Encapsulate Collection(封装集合):当你的类中有集合时,为了对该集合进行封装,你需要为集合创建相应的操作方法,例如增删改查等等。
- Replace Subclass with Fields(以字段取代子类):当你的各个子类中唯一的差别只在“返回常量数据”的函数上。当遇到这种情况时,你就可以将这个返回的数据放到父类中,并在父类中创建相应的工厂方法,然后将子类删除即可。
条件表达式重构规则(if-else)
有时候在实现比较复杂的业务逻辑时,各种条件各种嵌套。如果处理不好的话,代码看上去会非常的糟糕,而且业务逻辑看上去会非常混乱。通过一些重构规则来对条件表达式进行重构,可以让业务逻辑更为清晰,代码更以维护和扩展。
- Decompose Conditional(分解条件表达式):经if后的复杂条件表达式进行提取,将其封装成函数。
- Consolidate Conditional Expression(合并条件表达式):一些条件表达式后的语句体执行的代码块相同。
- Consolidate Duplicate Conditional Fragments(合并重复的条件片段):if与else中的相同语句。
- Remove Control Flag(移除控制标记):标记变量不易维护,不易理解。标记变量一般是可以使用其他语句进行替换的,可以使用break、return、continue等等,这个要根据具体情况而定。复杂的可以用状态机进行设计。
- Replace Nested Condition with Guard Clauses(以卫语句取代嵌套的条件):尽量不要将if-else进行嵌套,因为嵌套的if-else确实不好理解。要去除上面的嵌套模式,我们可以将if后的条件进行翻转,根据具体需求再引入return、break、continue等卫语句。
- Replace Condition with Polymorphism(以多态取代条件表达式):多态就是类的不同类型的对象有着不同的行为状态。当然,对多态再进一步包装,就是工厂设计模式。
继承关系重构规则
- Pull Up Field (字段上移) & Pull Down Field (字段下移)
- 字段上移:两个子类中的相同字段,向上移动。
- Extract Subclass (提炼子类):每个类的职责更为单一,即“单一职责”。
- Collapse Hierarchy (折叠继承关系):与“提炼子类”规则相对应。就是当你的父类与子类差别不大时,我们就可以将子类与父类进行合并。
- Form Template Method (构造模板函数):“模板”其实就是框架,没有具体的实现细节,只有固定不变的步骤,可以说模板不关心具体的细节。
- 以委托取代继承(Replace Inheritance with Delegation):子类只使用了父类的部分方法,而且没有继承或者部分继承了父类的数据。在这种情况下我们就可以将这种继承关系修改成委托的关系。具体做法就是修改这种继承关系,在原有子类中添加父类的对象字段,在子类中创建相应的方法,在方法中使用委托对象来调用原始父类中相应的方法。