重构是在不改变软件可观察性的前提下,改善软件的内部结构。程序员
第一章 重构,第一个案例
若是对一段代码添加一个新特性很麻烦,那就先重构这段代码,让添加新特性变得很容易,而后再添加新特性。
重构以前必须有一套可靠的测试机制,这些测试要么OK,要么即能列出失败清单,即具备自我检验能力。
重构技术就是以微小的步伐修改程序,并测试经过。
任何一个傻瓜均可以写出计算机理解的代码,但只有写出人类容易理解的代码,才是优秀的程序员。算法
第二章 重构原则
一、重构做名词,是一种对软件的调整;做动词,是一种改善软件结果的方法。
二、两顶帽子:添加新功能,重构,同一时间只能戴其中一个。添加新功能时,只要保证能经过测试,不重构;重构时再也不添加新功能,只改变程序结构,并让其经过测试。
三、重构能够而且应该用于如下目的:
改进软件设计:若是没有重构,随着功能的添加,程序会逐渐腐败,失去本身的结构,也越来越理解程序的设计。重构就像整理代码,使设计变得清晰。
使软件更容易理解:重构的过程至关于理解了软件的设计结构,这种理解是针对任何维护这个程序的人。另外一方面,能够用重构来帮助理解不熟悉的代码。
帮助找到bug:重构时能够深刻理解程序的行为,提升了发现bug的机会。
提升编程速度:良好的设计是快速开发的根本,重构正是一种产生良好设计的代码的手段。
四、什么时候重构:
三次原则 —— 事不过三,过则重构。
添加新功能时重构,这是最好的时机。
修补错误的时候重构
复审代码时重构,这在开发团队中有利于知识的传播和积累。结对编程、极限编程。
五、怎么对领导说为何重构:若是领导懂,那说不说无所谓;若是不懂,那就不说。
六、重构的难题:
关于接口的重构:保留原有接口,增长新接口。
难以经过重构的手法完成的设计改动:重构方案进行对比,若是有优点,那就使用它。
什么时候不应重构:重构的代价大于重写。项目接近于交付日期。
重构与设计:重构不能取代设计,设计也不能取代重构。设计的目的在于找到一个合适的初始解决方案,没有完美的设计,能够经过重构改善设计。
重构能够带来简单的设计,同时又不下降灵活性,这也下降了设计的难度。
重构与性能:重构可能会下降系统性能,但能够改善对软件的理解。若是系统性能的下降能够接收,那么就重构吧。
关于性能,可能大部分的时间消耗在不多的一些代码上,重点关注这些代码。在性能优化阶段,找出瓶颈点,经过不停地重构、测试、性能度量来进行优化。编程
第三章 代码的坏味道
味道的好坏要靠直觉来判断,没有理论上的定义,说哪一个代码是好的,哪一个是坏的。
下面是常见的一些坏味道的代码:
一、Duplicated Code 重复代码:提取成公共函数,在须要使用的地方调用。
二、Long Method 过长函数:分解该函数。若是说须要一段注释来解释一些代码的做用,那就将这些代码提炼到一个函数,并以其用途命令。这样比写注释好多了。
还有条件表达式和循环语句。
三、Large Class 过大的类
四、Long Parameter List 过长参数列,包括函数入参和函数内的临时变量
五、Divergent Change 发散式变化:对于模块来讲,针对外部不一样的变化类型,内部针对每种变化类型的修改应该越少越好。即一个类受多种变化的影响。
六、Shotgun Surgery 散弹式修改:对来模块来讲,针对一种外部的变化,内部涉及的修改的地方应该越少越好。即一个变化影响多个类。
七、Featura Envy 依恋情结
八、Data Clumps 数据泥团:好比类中的相同字段,函数的相同参数
九、Primitive Obsession 基本类型偏执
十、Switch Satatments switch惊悚现身:能够用多态
十一、Parallel Inheritance Hierarchies 平行继承体系
十二、Lazy Class 冗赘类
1三、Speculative Generality 夸夸其谈将来性
1四、Temporary Field 使人迷惑的暂时字段
1五、Message Chains 过多耦合的消息链
1六、Middle Man 中间人
1七、Inappropriate Intimacy 狎昵关系
1八、Alternative Classes with Different Interfaces 殊途同归的类
1九、Incomplete Library Class 不完美的库类
20、Data Class 纯稚的数据类
2一、Refused Bequest 被拒绝的遗赠
2二、Comments 过多的注释:当你须要写注释时,请尝试重构,让注释变得多余。设计模式
第四章 构筑测试体系
测试要是自动化的,让测试本身检查测试结果。这里所说的测试都是单元测试。
功能测试每次发现一个新的bug时,写一个单元测试来暴露这个bug。
测试你把握不大代码,和各类边界条件。
测试错误的时候,检查是不是预期的错误。
不要认为测试没法发现全部bug就不写测试,但它能发现大多数bug数组
第五章 重构列表
重构的基本技巧:小步前进,频繁测试。
重构与设计模式之间的关系与生俱来,模式是目标,重构是手段。性能优化
第六章 从新组织函数
一、Extract Method 提炼函数:将一段代码单独提取出来,并以用途命名。重点在于对局部变量的优化,包括传进源函数的参数和源函数内的临时变量。
二、Inline Method 内联函数:若是函数本体和函数名同样清晰,且本体很简短,那么就用本体替代函数。
三、Inline Temp 内联临时变量:若是一个变量只被使用一次,那么尝试内联它。
四、Replace Temp with Query 以查询代替临时变量:若是一个临时变量只保存某一表达式的结果,将表达式提取成函数,将对临时变量的引导转换成对函数的调用。
五、Introduce Explaining Variable 引入解释性变量:对于一个复杂的表达式,将表达式或其中的一部分的结果放进一个临时变量,并以变量名来解释表达式的用途。
六、Split Temporary Variable 分解临时变量:某个临时变量被赋值屡次,它既不是循环变量,也不用于收集计算结果,那么针对每次赋值,建立一个新的临时变量。
即让临时变量只承担一次责任。
七、Remove Assignment to Parameters 移除对参数的赋值:代码对一个参数进行赋值,能够用一个临时变量取代参数。
八、Replace Method with Method Object 以函数对象取代函数:有一个大型函数,没法对局部对象使用Extract Method,那么将这个函数放进一个对象内,如此依赖局部变量
就变成对象内的字段。而后在对象内将函数分解成小函数。
九、Substitute Algorithm 替代算法:想要把某个算法替换成更清晰的算法,那么将函数本体替换成算法。session
第七章 在对象之间搬移特性
一、Move Method 搬移函数:一个函数与其所驻类以外的另外一个类进行更多的交流(调用后者,或者被后者调用),那么在该函数最常引用的类中
创建一个有着相似行为的新函数。将旧函数变成一个单纯的委托函数,或者彻底移除。
二、Move Field 搬移字段:某个字段被其所驻类以外的另外一个更多地用到。在目标类新建一个字段,修改源字段的全部用户,令它们改用新字段。
三、Extract Class 提炼类:某个类作了应该由两个类作的事。创建一个新类,将相关的字段和函数从旧类搬移到新类。
四、Inline Class 内联类:某个类没有作太多事情。将这个类的全部特性搬移到另外一个类中,而后移除原类。
五、Hide Delegate 隐藏委托关系:客户经过一个委托类来调用另外一个对象。在服务类上创建客户所需的全部函数,用以隐藏委托关系。
六、Remove Middle Man 移除中间人:某个类作了过多的简单委托动做。让客户直接调用受托类。
七、Introduce Foreign Method 引入外加函数:须要为提供服务的类增长一个函数,但没法修改这个类。在客户类中创建一个函数,并以第一参数
形式传入一个服务类实例。
八、Introduce Local Extension 引入本地扩展:须要为服务类提供一些额外函数,但没法修改这个类。创建一个新类,使它包含这些额外函数。
让这个扩展品成为源类的子类或包装类。app
第八章 从新组织数据
一、Self Encapsulate Field 自封装字段:你直接访问一个字段,但与字段之间的耦合关系逐渐变得笨拙。为这个字段创建取值/设置函数,
并以这些函数来访问这个字段。
二、Replace Data Value with Object 以对象取代数据值:你有一个数据项,须要与其余数据和行为一块儿使用才有意义。将数据项变成对象。
三、Change Value to Reference 将值对象改成引用对象:你从一个类衍生出许多彼此相等的实例,但愿将它们替换为同一个对象。
将这个值对象变成引用对象。
四、Change Reference to Value 将引用对象改为值对象:你有一个引用对象,很小且不可变,并且不易管理。将它变成一个值对象。
值对象有一个很是重要的特性:它们应该是不可变的。
五、Replace Array with Object 以对象取代数组:你有一个数组,其中的元素各自表明不一样的东西。以对象替代数据。
对于数组中的每一个元素,以一个字段来表示。
六、Duplicate Observed Data 复制被监视数据:你有一些领域数据置身于GUI控件中,而领域函数须要访问这些数据。
将该数据复制到一个领域对象中。创建一个Observer模式,用以同步领域对象和GUI对象内的重复数据。
七、Change Unidirectional Association to Bidirectional 将单向关联改为双向关联:两个类都须要使用对方特性,但其间只有一条单向链接。
添加一个反向指针,并使修改函数可以同时更新两条链接。
八、Change Bidirectional Association Unidirectional 将双向关联改为单向关联:两个类之间有双向关联,但其中一个类现在再也不须要另外一个类的特性。
去除没必要要的关联。
九、Replace Magic Number with Symbolic Constant 以字面常量取代魔法数:你有一个字面数值,带有特别含义。
创造一个常量,根据其意义为它命名,并将上述的字面数值替换为这个常量。
十、Encapsulate Field 封装字段:你的类中存在一个public字段。将它声明为private,并提供相应的访问函数。
十一、Encapsulate Collection 封装集合:有一个函数返回一个集合。让这个函数返回该集合的一个副本,并在这个类中提供添加/移除集合元素的函数。
十二、Replace Record with Data Class 以数据类取代记录:你须要面对传统编程环境中的记录结构。为该记录建立一个哑数据对象。
1三、Replace Type Code with Class 以类取代类型码:类之中有一个数值类型码,但它并不影响类的行为。以一个新类替换该数值类型码。
1四、Replace Type Code with Subclasses 以子类取代类型码:你有一个不可变的类型码,它会影响类的行为。以子类取代这个类型码。
1五、Replace Type Code with State/Strategy 以State/Strategy取代类型码:你有一个类型码,它会影响类的行为,但你没法经过继承手法消除它。
以状态对象取代类型码。
1六、Replace Subclasswith Fields 以字段取代子类:你的各个子类的惟一差异只在”返回常量数据“的函数身上。
修改这些函数,使它们返回超类中的某个(新增)字段,而后销毁子类。ide
第九章 简化条件表达式
一、Decompose Conditional 分解条件表达式:你有一个复杂的条件语句。从if、then、else三个段落中分别提炼出独立函数。
二、Consolidate Conditional Expression 合并条件表达式:你有一系列条件测试,都获得相同结果。
将这些测试合并为一个条件表达式,并将这个条件表达式提炼成一个独立函数。若是这些条件表达式之间彼此独立,那么慎用本项重构。
三、Consolidate Duplicate Conditional Fragments 合并重复的条件片断:在条件表达式的每一个分支上有着相同的一段代码。
将这段代码搬移到条件表达式以外。
四、Remove Control Flag 移除控制标记:在一系列布尔表达式中,某个变量带有控制标记的做用。以break或return取代控制标记。
五、Replace Nested Conditional with Guard Clauses 以卫语句取代嵌套条件表达式:函数中的条件逻辑令人难以看清正常的执行路径。
使用卫语句表现全部特殊状况。精髓就是给某条分支以特别的重视,来表达该条件十分罕见,应该单独检查。
每一个函数一个入口一个出口:入口最好是一个,出口则不必定要是一个,保持代码清晰才是关键。
六、Replace Conditional with Polymorphism 以多态取代条件表达式:你手上有个条件表达式,它根据对象类型的不一样而选择不一样的行为。
将这个条件表达式的每一个分支放进一个子类内的复写函数中,而后将原始函数声明为抽象函数。
七、Introduce Null Object 引入Null对象:你须要再三检查某对象是否为Null。将Null值替换为Null对象。
八、Introduce Assertion 引入断言:某一段代码须要对程序状态作出某种假设。以断言明确表现这种假设。
断言的价值在于:帮助程序员理解代码正确运行的必要条件。不能滥用断言,不要使用它来检查“你认为应该为真”的条件,请只使用
它来检查“必定必须为真”的条件。函数