3.4 Long Parameter List(过长参数列表)
3.9 Primitive Obsession (基本类型偏执)
3.10 Switch Statements (switch 惊悚现身)
3.11 Parallel Inheritance Hierarchies (平行继承体系)
3.13 Speculative Generality (夸夸其谈未来性)
3.14 Temporary Field (令人迷惑的暂时字段)
3.15 Message Chains (过度耦合的消息链)
3.17 Inappropriate Intimacy (狎昵关系)
3.18 Alternative Classes with Different Interfaces (异曲同工的类)
3.19 Incomplete Library Class (不完美的库类)
5.4 Replace Temp With Query(以查询替换临时变量)
5.5 Introduce Explaining Variable(引入解释性变量)
6.6 Spilt Temporary Variable(分解临时变量)
5.7 Remove Assignments to Parameters(移除对参数的赋值)
5.8 Replace Method with Method Object (以函数对象取代函数)
5.9 Substatute Algorithm (替换算法)
6.7 Introduce Foreign Method (引入外加函数)
6.8 Introduce Local Extension (引入本地扩展)
7.1 Decompose Conditional (分解条件表达式)
7.2 Consolidate Conditional Expression(合并条件表达式)
7.3 Consolidate Duplicate Conditional Fragments(合并重复的条件片段)
7.4 Remove Control Flag (移除控制标记)
7.5 Replace Nested Conditional with Guard Clause(以卫语句取代嵌套条件表达式)
7.6 Replace Conditional with Polymorphic(以多态取代条件表达式)
7.7 Introduce Null Object(引入Null 对象)****
7.8 Introduce Assertion (引入断言)
1.影片租赁
===========================================Before Recontruct========================================
============================================reconstruction1==========================================
=============================================reconstruction2=========================================
*************
PS : 为什么将租期长度(daysRented)传给Movie对象,而不是将影片类型传给Rental对象呢????
因为本系统可能发生的变化是加入新的影片类型,这种变化带有不稳定倾向。如果影片类型有所变化,希望尽可能控制他造成的影响,所以选择在Movie对象中添加计算费用方法(computeAmout(daysRented)),同样的办法处理积分方法。
*************
2.重构原则
重构 VS 性能优化
1. 重构的目的是使软件更容易被理解和修改。
2. 性能优化通常不会改变组件的行为(除了执行速度),只会改变其内部结构。
《心灵鸡汤》 为什么重构?
重构改进软件设计,重构使得软件更容易理解,重构帮助找到bug,重构提高编程速度。
何时重构?三次法则:事不过三,三则重构。
添加功能时重构,修补错误时重构,复审代码时重构。
3.代码的坏味道
3.1 Duplicated Code (重复代码)
3.2 Long Method (过长函数)
3.3 Large Class (过大的类)
3.4 Long Parameter List(过长参数列表)
3.5 Divergent Change(发散式变化)
3.6 Shotgun Surgery (霰弹式修改)
3.7 Feature Envy (依恋情结)
最根本的原则是:将总是一起变化的东西放在一块儿。数据和引用这些数据的行为总是一起变化的,但也有例外。如果例外 出现我们就搬移那些行为,保持变化只在一地发生。
3.8 Data Clumps (数据泥团)
3.9 Primitive Obsession (基本类型偏执)
对象的一个极大的价值在于:它们模糊(甚至打破)了横亘于基本数据和体积较大的类之间的界面。
3.10 Switch Statements (switch 惊悚现身)
面向对象程序的一个最明显特征就是:少用switch(或case)语句。从本质上说,switch语句的问题在于重复。
3.11 Parallel Inheritance Hierarchies (平行继承体系)
3.12 Lazy Class (冗赘类)
3.13 Speculative Generality (夸夸其谈未来性)
如果函数或类的唯一用户是测试用例,这就飘出了坏味道Speculative Generality。如果你发现这样的函数或类,请把它们连同其测试用例一并删掉。但如果它们的用途是帮助测试用例检测正当功能。
3.14 Temporary Field (令人迷惑的暂时字段)
3.15 Message Chains (过度耦合的消息链)
3.16 Middle Man (中间人)
3.17 Inappropriate Intimacy (狎昵关系)
3.18 Alternative Classes with Different Interfaces (异曲同工的类)
3.19 Incomplete Library Class (不完美的库类)
3.20 Data Class (纯稚的数据类)
3.21 Refused Bequest (被拒绝的遗赠)
3.22 Comments (过多的注释)
======================================================================================
4. 重构列表
每个重构手法都有如下5个部分:
重构的基本技巧:小步前进、频繁测试。
5. 重新组织函数
Extract Method :把一段代码从原先函数中提取出来,放进一个单独函数中。
Inline Method : 将一个函数调用动作替换为该函数本体。
如果在进行多次提炼之后,意识到提炼所得的某些函数并没有做任何实质事情,或如果需要回溯回复原先函数,就需要Inline Method。
5.1 Extract Method
======================================Extract Method 后======================================
5.2 Inline Method
5.3 Inline Temp
5.4 Replace Temp With Query(以查询替换临时变量)
临时变量的问题:它们是暂时的,而且只能在所属函数内使用。
由于临时变量只在所属函数内可见,所以它们会驱使你写出更长的函数,因为只有这样才能访问到需要的临时变量。如果把临时变量替换为一个查询,那么同一个类中的所有函数都将可以获得这份信息。
=====================================重构前===========
==================================重构后====================================
5.5 Introduce Explaining Variable(引入解释性变量)
===============================重构前======================================
==============================Introduce Explaining Variable 后=================
============================Extract Method 后===============================
那么问题来了,应该在什么时候使用Introduce Explaining Variable呢?
在Extract Method 需要花费更大工作量时(一个拥有大量局部变量的算法时),可以使用该方法来重构。然后再运用Replace Temp With Query 把中间引入的那些解释性临时变量去掉。
6.6 Spilt Temporary Variable(分解临时变量)
5.7 Remove Assignments to Parameters(移除对参数的赋值)
5.8 Replace Method with Method Object (以函数对象取代函数)
5.9 Substatute Algorithm (替换算法)
6. 在对象之间搬移特性
6.1 Move Method (搬移函数)
如果我想加入的是一两个函数,就会使用Introduce Foreign Method;如果加入的不止一两个函数,就使用Introduce Local Extension。
例子:
将daysOverdrawn作为参数传递给AccountType
6.2 Move Field (搬移字段)
在目标类新建一个字段,修改源字段的所有用户,令它们改用新字段。
6.3 Extract Class (提炼类)
6.4 Inline Class(将类内联化)
6.5 Hide Delegate(隐藏“委托关系”)
6.6 Remove Middle Man (移除中间人)
6.7 Introduce Foreign Method (引入外加函数)
你需要为提供服务的类增加一个函数,但你无法修改这个类。在客户类中建立一个函数,并以第一参数形式传入一个服务类实例。
6.8 Introduce Local Extension (引入本地扩展)
subclassing(子类化)和wrapping(包装类)统称为本地扩展(local extension)
第7章 重新组织数据
7.1 Decompose Conditional (分解条件表达式)
争议:条件如果很少还需要提炼出来么???!!!
7.2 Consolidate Conditional Expression(合并条件表达式)
--------------------------------------**** 经常用到*****-----------------------------------------------------------------
--------------------------------------**** 经常用到*****-----------------------------------------------------------------
7.3 Consolidate Duplicate Conditional Fragments(合并重复的条件片段)
7.4 Remove Control Flag (移除控制标记)
结构化编程原则:每个子程序只能有一个入口和一个出口。单一出口并不是很赞同。
7.5 Replace Nested Conditional with Guard Clause(以卫语句取代嵌套条件表达式)
条件表达式:1. 所有分支都属于正常行为。 2. 条件表达式提供的答案中只有一种是正常行为,其他都是不常见的情况。
7.6 Replace Conditional with Polymorphic(以多态取代条件表达式)
将这个条件表达式的每个分支放进一个子类内的复写函数中,然后将原始函数声明为抽象函数。
多态:如果你需要根据对象的不同类型而采取不同的行为,多态使你不必编写明显的条件表达式。类型码的switch语句、基于类型名称的if-then-else 语句都很少见了。
如果若干switch语句针对的是同一类类型码,你只需要针对这个类型码建立一个继承结构就行了。
7.7 Introduce Null Object(引入Null 对象)****
多态的最根本好处在于:你不必再向对象询问“你是什么类型“而后根据得到的答案调用对象的某个行为--你只管调用该行为就是了,其他的一切多态机制会为你安排妥当。
7.8 Introduce Assertion (引入断言)
只有当某个条件为真时,该段代码才能正常运行。
如果你发现代码假设某个条件始终为真,就加入一个断言明确说明这种情况。(你可以新建一个Assert类,用于处理各种情况下的断言)
Assert类应该有多个函数,函数名称应该帮助程序员理解其功用。除了isTrue()之外,你还可以加上equals()和shouldNeverReachHere()等函数。
**********************************************************后续见Part2*************************