Part 1 实例
- 将大的函数拆分成小函数 (快捷键 command+option+M)
2.变量以及方法命名
3.函数应该放在它所使用的数据所属的对象内(这个方法用了哪些数据,就放在数据对应的类下面)
4.尽量减少临时变量
5.多态取代switch
Part 2 概念
1.重构和新增功能不同时进行
2.何时重构:
1》.随时重构,当你想做某个功能,重构可以让这个功能更方便,或者说做的更好
2》.第三次写重复代码就可以重构
3》.添加功能的时候
4》.bug的时候
5》.Code Review的时候
Part 3 代码的坏味道
1.duplicated code
2.long Method :
解决方式: 1. 提炼 注释的地方
2. 提炼循环
3.Large Class
4.Long Parameter List
5.Divergent Change (发散式变化) ——一个类受多个变化影响
6.Shotgun Surgery (霰弹式变化) ——一种变化影响多个类修改
7.Feature Envy (依恋情节) —— 一个类使用了多份数据,就放在用的数据最多的那个类里
8.Data Clumps (数据泥团)
9.Primitive obsession(基本类型偏执)
10.switch statement
解决方式:多态
11.Parallel Inheritance Hierarchies (平行继承体系)—— 当给某个类增加了子类,必须发给其他类也增加子类
解决方式:让一个继承体系实例引用另一个继承体系的实例
12.Lazy Class(冗赘类)—— 就是意义不大的类
13.Speculative Generality(夸夸其谈未来性)—— 过多设计导致的东西
14.Temporary Field
15.message Chain(过度耦合的消息链)
16.middle man(中间人)—— 过度委托
解决方式:1.去掉过度委托,直接调用
2. 如果middle man 有其他行为,可以优化成子类
- Inappropriate Intimacy (狎昵关系)——类之间的过于依赖
18.Alternative Classes with Different Interfaces(异曲同工的类)
解决方式:1.Extract Superclass
19.Incomplete Library Class (不完美的库类)
20.Data CLass(纯稚的数据类)
21.refused bequest(被拒绝的遗赠) - Comments(过多的注释)
解决方式:1.好的方法命名代替注释
2.introduce assertion
Part 4 关于测试的一些思考
1.开发应该专注于单元测试,假设其他包都正常,对每个包单独测试
2.当功能测试出现bug的时候,不仅仅应该修改bug,还需要用一个单元测试来暴露这个bug
3.寻找边界条件
Part 6
1.Extract Method
- Inline Method
- Inline Temp(内联临时变量)——用方法代替临时变量
eg: 1. if中的条件判断 应该提炼成一个方法 - replace temp with query(以查询取代临时变量)
eg:临时变量只被赋值一次(如何检查:1.将目标临时对象声明为final) - Introduce Explaining Variable(引入解释性变量)—— 复杂的表达式用临时变量代替(但是也可以用Extract Method,区别:局部变量使用多,提取成本较大就用Introduce Explaining Variable)
- Split Temporary Variable(分解临时变量)—— 除了循环用的临时变量,一般的临时变量应该只被赋值一次,如果有多次,需要判断意义是否一样,不一样应该用一个新的临时变量去装载
- Remove Assignments to Parameters(移除对参数的赋值)—— 用临时变量去装这个赋值
- Replace Method with Method Object —— 把一个复杂函数(用了太多临时变量的函数)提取成一个class< A class >, 把所有临时变量都放入final 字段里。然后再原来方法调用的地方,使用委托的方式,让新的类的方法被调用<eg:new A().getContant()>, 然后就可以在A class 里面进行Extract Method
- Substitute Algorithm(替换算法)—— 将复杂的算法 用简单的算法,更加易读的算法代替
Part 7
- move Method—— 当一个类的行为过多,或者一个类和另外一个类高度耦合,就可以使用这个方式
- move field
- Extract Class—— 根据不同的责任划分类
- Inline Class
- Hide delegate(隐藏委托关系)—— 对外部隐藏某个调用关系
- Remove Middle Man —— 移除过多的委托关系(出现大量委托关系的时候,可以直接暴露相关对象)
- Introduce Foreign Method(引入外加函数)—— 当出现大量对某个函数的调用使用同一个值,且参数较多的情况,可以将这个调用过程Extract Method ,这个函数叫做被调用函数的外加函数
- Introduce Local extension(引入本地扩展)—— 当 外加函数较多的时候,应该使用Introduce Local extension,有2种做法:① 子类 继承,super() ② 包装类 使用 委托
Part 8
- Self Encapsulate Field(自封装字段)—— 在类内部也使用使用get set方法获取字段的值
- Replace Data Value with Object(以对象取代数据值)—— 数据对象复杂,或者数据关系较多,应该封装成一个对象
- Change Value to Reference (将值对象改为引用对象)
- Change Reference to Value (将引用对象改为值对象)
- Replace Array with Object —— 数组应该用于“以某种顺序容纳一组相似对象”,如果涉及到一个东西的多个属性,多个对象 就应该改为object
- Duplicate Observe Data (复制被监视的数据)
- Change Unidirectional Association to Bidirectional(将单项关联改为双向关联)
- Change Bidirectional Association to Unidirectional(将双向关联改为单项关联)
- Replace Magic Number with Symbolic Constant(用字面常亮取代魔法数)—— 就是用 static final + 类型+ 名称 = 魔法数
- Encapsulate Field(封装字段)—— 把public 字段设置为private,使用get set函数公开出去
- Encapsulate Collection(封装集合)—— 不要把整个集合公开出去,只是把需要公开的 add 方法公开出去,比如 新增集合里的一个元素,对外的应该只有
public void addCourse(object obj){
courses.add(obj);
}
只会把obj 扔出去,
- Replace Record with Data Class(以数据类代替记录)
- Replace Type Code with Class(以类取代类型码)
- Replace Type Code with Subclasses —— 面对swich 这种判断需要的就可以把type code 使用子类来重构
- Replace Type Code with State/Strategy
- Replace Subclass with Fields(用字段取代子类)—— 子类中如只有一个常量函数,那么久没有必要用子类。可以直接用字段来代替
Part 9
- Decompose Conditional(分解条件表达式)—— 把 if,then, else中的条件语句提取出来
- Consolidate Condition Expression(合并条件表达式)—— 就是把结果一致的条件放在一起
- Consolidate Duplicate Conditional Fragments(合并重复的条件片段)—— 有共同的执行提取出来
- Remove Control Flag—— 用 break or continue 代替
- Replace Nested Conditional with Guard Clauses(以卫语句取代嵌套条件表达式)—— 卫语句:单独的某个检查,如果为真立刻返回。检查提前,对特殊情况单独处理
- Replace Conditional with Polymorphism(以多态取代条件表达式)—— 继承代替switch
- Introduce Null Object (引入null对象)—— 把返回null的地方用一个空对象代替, 这个空对象可以处理默认值
- Introduce Assertion(引入断言)—— 作为调试和辅助开发,提前判断结果,最后会删除
Part 10
- Rename Method —— 良好的命名方式
- Add Parameter
- Remove Parameter
- Separate Query from Modifier (分离查询函数和修改函数)—— 把修改对象的方法和仅是查询数据的方法分开
- Parameterize Method (令函数携带参数)—— 用参数减少重复代码
- Replace Parameter with Explicit Method (用明确函数取代参数)—— 用多个函数取代 带有多个意义的单个函数,当某个函数出现大量if else类似的判断就需要用这种方式重构
- Preserve Whole Object(保持对象完整)—— 当某个函数大量用到调用方的某个对象的参数的时候,应该使用对象,而不是拆解出来的多个值
- Replace Parameter with Methods(用函数取代参数)—— 当某个参数是从其他函数取得的时候,应该直接在方法函数内部自行调用,不要从参数传入
- Introduce Parameter Object(引入参数对象)—— 当某一组参数被大量传递,应该用个类把他们包裹起来传递
- Remove Setting Method (移除设值函数)—— 如果不对外公开set方法,该值仅通过构造方法设值有效, 就移除set参数
- Hide Method—— 不必要的函数设置为private
- Replace Constructor with Factory Method(工厂模式代替构造函数)—— Class.forName
- Encapsulate Downcast(封装向下转型)—— 将向下转型的动作移到方法里
- Replace Error Code with Exception(用异常取代错误码)
- Replace Exception with Test(用测试取代异常)—— 异常应该是用于罕见,异常的行为,而不是使用在条件检查的地方,把某些正常行为的处理,使用条件语句放在前面
Part 11
- Pull Up Field(字段上移)—— 子类公有的字段可以移到超类
- Pull Up Method(函数上移)—— 子类中完全相同结果的函数可以移到超类
- Pull Up Constructor Body (构造函数本体上移) —— 子类中相同的构造函数移动到超类
- Push Down Method (函数下移)—— 当超类中的某个函数只于部分子类相关
- Push Down Field(字段下移)—— 当超类中某个字段只于特定子类相关
- Extract Subclass (提炼子类)—— 某些特性 只被部分实例使用,应该移到子类中去
- Extract Superclass (提炼超类)—— 两个类有相似特性类,可以提炼出公共部分,移到超类里去
- Extract Interface (提炼接口)—— 多个类提供相同功能的时候(比如租借接口,然后电脑和磁带实现该接口,提供不同的计算方式)
- Collapse Hierarchy (折叠继承体系)—— 超类和子类区别不大的时候,应该合并
- Form Template Method (塑造模板函数)—— 就是多个类流程一致,但是每个流程实现方式不一致的时候,用 ’模板方法模式‘ 代替(父类规定一个invoke 函数,和其他抽象函数,子类实现不同的抽象函数)
- Replace Inheritance with Delegate(委托取代继承)—— 子类只使用超类中的一部分数据,而且不需要继承的数据
- Replace Delegate with Inheritance(继承取代委托)—— 两个类之间属于委托关系,但是大部分是简单的委托函数,类似直接调用被委托的类的函数(但是如果没有使用到受托类的所有函数就应不应该使用该方法)
Part 12
- Tease Apart Inheritance(梳理并分解继承体系)
- Convert Procedural Design to Objects(将过程化设计转为对象设计)
- Separate Domain from Presentation (将领域和表述/显示分离)
- Extract Hierarchy (提炼继承体系)