重构——改善既来代码的统筹

重构——改善既来代码的宏图
Refactoring——Improve the Design of Existing Code
作者: 马丁 富勒Martin Fowler


平、什么是重构?

所谓重构大凡这么一个过程在不改动代码外在表现之前提下,对代码作出修改,以精益求精程序的内部结构。本质上说,重构就是以代码写好以后改进其的筹划

重构(名词):本着软件内部结构的同一种植调整,目的是在不移软件可察行为之前提下,提高该可理解性,降低其修改成本
重构(动词):运同一文山会海重构手法,在未转移软件而察行为之前提下,调整其组织

重构的目的是设若软件再易于被了解和改动。重构未会见转软件可观察的行为——重构之后软件功能还是。

重构技术就是是以轻的步子修改程序,如果你作下错,很轻就可以发现它。


仲、为何重构?

(1)
重构改进软件设计。如果无重构,程序的统筹会逐渐堕落变质。重构很像是当整治代码,你所开的尽管是被具有东西回去应出的职务上。经常性的重构可以助维持自己该有的造型。

(2)重构使软件再便于掌握。重构可以帮助我们于代码更易读。

(3)重构帮助找到bug。对代码进行重构,可以扶持我们深刻理解代码,对代码理解的愈发怪,就越来越会帮忙我们找到bug。重构能够助咱重新管用地勾画有健康的代码。

(4)重构提高成为速度。重构可以帮忙我们更高效地开发软件,因为它阻挡系统腐败变质,它甚至还足以提高统筹质量。


老三、何时重构?

重构不是一致件理当特别扭出时召开的作业,重构应该随时随地进行。不应为重构而重构,之所以重构,是因我们怀念做别的啊事,而重构可以帮助我们把那些从做好。

其三糟模拟虽:从非了三,三虽然重构

(1)增长效果时重构。
(2)补错误时重构。
(3)复审代码时重构。


季、何时无该重构?

代码根本无法工作还是太糟糕,重构还未使重写来的大概

在列之终极期限,应该避免重构


五、代码的坏味道

  • 再代码(Duplicated Code)
  • 过长函数(Long Method)
  • 过非常的接近(Large Class)
  • 过长参数列(Long Parameter List)
  • 发散式变化(Divergent Change):一个类受多种扭转之震慑
  • 霰弹式修改(Shotgun Surgery):一种转移引发多单类似对应修改
  • 恋情结(Feature Envy):函数对有类的兴趣高过好所处类的兴味
  • 数泥团(Data
    Clumps):相同之几何宗数据出现在不同地方,这些绑在一块儿出现的数应发属于她自己的对象
  • 中心项目偏执(Private
    Obsession):很多人非情愿在微任务及动小目标
  • switch惊悚现身(Switch
    Statements):switch语句会在过剩地方再出现,一改则需全改
  • 平行继承体系(Parallel Inheritance
    Hierarchies):当您啊某个一个类增加子类时,也须也另外一个好像对应增加一个看似
  • 冗赘类(Lazy Class):如果一个像样非值得在,那即便于它消失
  • 夸夸其谈的前景星(Speculative
    Generality):预留的不算的抽象类,无用的泛参数
  • 让人迷惑的暂行字段(Temporary
    Field):类吃之一只字段只也某些特殊情形如果设置
  • 过于耦合的消息链(Message
    Chains):用户向一个目标要另一个目标,然后还往后者要另一个对象……
  • 中间人(Middle Man):无用的托,过多之中间层
  • 性感关系(Inappropriate
    Intimacy):两只类似过于亲近,一个接近过于关注其他一个接近的成员
  • 如出一辙之类似(Alternative Classes with Different
    Interfaces):不同名字的好像还是函数,作者相同的从事
  • 不圆满的库类(Incomplete Library Class):类库设计不容许圆
  • 纯数据类(Data
    Class):一个接近有一些字段以及用于访问这些字段的函数,除此之外一无长物
  • 给拒的遗赠(Refused
    Bequest):子类不思量继续超类所有的函数和数目,只想挑几样来打
  • 过多之诠释(Comments)

六、构筑测试系统

  1. 重构的第一前提是有所一个可靠的测试环境
  2. 假若写好一些效能,就当即添加测试,并保管有测试都统统自动化,让她检查自己的测试结果。一模拟测试就是一个强硬的bug侦测器,能够大大削减查找bug所待的光阴。
  3. 著测试代码的最为实惠时机是当上马编程之前。当你需要添加特性的时节,先勾勒相应测试代码。编写测试代码其实就算是在提问自己:添加这个效果要开来什么。编写测试代码还会而你管注意力集中让接口而不实现。预先写好的测试代码也为公的干活设置一个强烈的截止标志:一旦测试代码正常运转,工作就可了了。
  4. 大多用单元测试。测试你无比担心失误的地方,考虑或者出错的分界条件。不要因测试无法捕捉所有bug就无写测试,因为测试的确可捕捉到大多数bug。“花合理时间抓来大多数bug”要好了“穷尽一生抓来有bug”。

七、重新组织函数

  1. 提炼函数(Extract
    Method)。你出同等段落代码可以为集体以一块儿并独立出来。将立即段代码放上一个独自函数中,并拿函数名称解释该函数的用。
  2. 内联函数(Inline
    Method)。一个函数的本体和名一致清楚易掌握。在函数调用点插入函数本体,然后变除该函数。
  3. 内联临时变量(Inline
    Temp)。你发出一个即变量,只受一个简单易行表达式赋值一潮,而它妨碍了别样重构手法。将所有对该变量的援动作,替换为对它们赋值的老表达式自身。
  4. 坐询问取代临时变量(Replace Temp with
    Query)。你的次因一个即变量保存有一样表达式的演算结果。将之表达式提炼到一个单身函数中。将以此临时变量的备引用点替换为对新函数的调用。此后,新函数就可为另外函数使用。
  5. 引入解释性变量(Introduce Explaining
    Variable)。你生出一个苛的表达式。将欠复杂表达式(或中有些)的结果放上一个即变量,以此变量名称来分解表达式用途。
  6. 讲临时变量(Split Temporary
    Variable)。你的主次来有临时变量被赋值过同样涂鸦,它既是无是循环变量,也未为用来收集计算结果。针对每次赋值,创造一个单身、对应之旋变量。
  7. 移除对参数的赋值(Remove Assignments
    Parameters)。代码对一个参数进行赋值。以一个即变量取代参数的位置。
  8. 以函数对象取代函数(Replace Method with Method
    Object)。你闹一个重型函数,其中针对一些变量的运要您无法使用Extract
    Method。将之函数放上一个单身对象中,如此一来局部变量就改为了对象内的字段。然后你得以跟一个目标被拿此特大型函数分解为多只袖珍函数。
  9. 替换算法(Substitute
    Algorithm)。你想只要拿某部算法替换为其它一个再度清晰的算法。将函数本体替换为外一个算法。

八、在对象期间搬移特性

  1. 搬迁移函数(Move
    Method)。你的顺序中,有只函数和那个所驻之外的另外一个接近进行重复多交流:调用后者,或为后世调用。在该函数最经常引用的切近中起一个具近乎表现之新函数。将原本函数变成一个独自的嘱托函数,或是将故函数完全移除。
  2. 搬迁移字段(Move
    Field)。你的次序中,某个字段被那个所驻类之外的另一个好像更多地用到。在目标类新建一个字段,修改来自字段的有着用户,令它改用新字段。
  3. 提炼类(Extract
    Class)。某只八九不离十做了当产生少个像样做的从业。建立一个新类,将有关的字段和函数从旧类搬移到新类。
  4. 拿接近内联化(Inline
    Class)。某个类没有召开顶多事情。将是仿佛的备特性搬移到其他一个类似中,然后移除原类。
  5. 暗藏“委托关系”(Hide
    Delegate)。客户通过一个寄来调用另一个对象。在劳动类似及建立客户所待的拥有函数,用以隐藏委托关系。
  6. 移除中间人(Remove Middle
    Man)。某只近乎做了过多之简练委托动作。让客户直接调用受托类。
  7. 引入外加函数(Introduce Foreign
    Method)。你需要呢提供服务之接近增加一个函数,但若无法修改者看似。在客户类吃确立一个函数,并坐率先参数形式传播一个服务类实例。
  8. 引入本地扩展(Introduce Local
    Extension)。你用吗服务类提供一些附加函数,但你无法修改是类似。建立一个新类,使它们涵盖这些额外函数。让这扩展品成为源类的子类或包装类。

九、重新组织数据

  1. 起封装字段(Self Encapsulate
    Field)。你直接访问一个字段,但和字段之间的耦合关系日趋变得笨拙。为这个字段建立取值/设值函数,并且独自因这些函数来拜访字段。
  2. 为目标取代数据值(Replace Data Value with
    Object)。你闹一个数目项,需要与另外数据以及表现并使用才来意义。将数据项改成对象。
  3. 拿价值对象改吧援对象(Change Value to
    Reference)。你自一个好像衍生出广大相相当的实例,希望以它们替换为跟一个目标。将此价值对象变成引用对象。
  4. 用引用对象改吧价值对象(Change Reference to
    Value)。你产生一个引用对象,很有点且不可变,而且是管理。将其成一个值对象。
  5. 以目标取代数据(Replace Array with
    Object)。你有一个数组,其中的元素分别代表不同之物。以目标替换数组,对于数组中的每个元素,以一个字段来代表。
  6. 复制“被监视数据”(Duplicate Observed
    Data)。你闹局部世界数据在GUI控件被,而世界函数需要看这些数量。将该数量复制到一个天地对象中。建立一个Observe模式,用以同步领域对象及GUI对象内之双重数据。
  7. 以独自为关系改吗双向关联(Change Unidirectional Association to
    Bidirectional)。两个像样都待动用对方特性,但里只发同等长长的仅为链接。添加一个反向指针,并而修改函数能够同时创新两长链接。
  8. 以双向关联改吗单独为关系(Change Bidirectional Association to
    Unidirectional)。两个像样里产生双向关联,但中一个类如今不再需要另一个好像的特征。去除不必要的干。
  9. 盖字面常量取代魔法数(Replace Magic Number with Symbolic
    Constant)。你生出一个字面数值,带有特别意义。创造一个常量,根据那意义吗它定名,并拿上述的字面数值替换为夫常量。
  10. 封装字段(Encapsulate
    Field)。你的接近吃有一个public字段。将它声明也private,并提供对应的拜会函数。
  11. 卷入集合(Encapsulate
    Collection)。有只函数返回一个集。让这函数返回该集的一个只读副本,并以这看似中提供丰富/移除集合元素的函数。
  12. 坐数据类取代记录(Replace Record with Data
    Class)。你需要直面传统编程环境被的记录组织。为该记录创建一个“哑”数据对象。
  13. 坐类取代类型码(Replace Type Code with
    Class)。类里产生一个数值类行码,但她并无影响类的作为。以一个新的类似替换该数值类型码。
  14. 坐子类取代类型码(Replace Type Code with
    Subclass)。你而且一个不可变的类型码,它见面影响类的一言一行。以子类取代这个类型码。
  15. 为State/Strategy取代类型码(Replace Type Code with
    State/Strategy)。你出一个类型码,它会潜移默化类的行,但你无法透过持续手法消除其。以状态对象取代类型码。
  16. 为字段取代子类(Replace Subclass with
    Fields)。你的顺序子类的唯一差别就以“返回常量数据”的函数身上。修改这些函数,使他么返回超类中之有(新增)字段,然后销毁子类。

十、简化条件表达式

  1. 释疑条件表达式(Decompose
    Conditional)。你闹一个错综复杂的基准(if-then-else)语句。从if、then、else三细分段落中分头提炼出单身函数。
  2. 合条件表达式(Consolidate Conditional
    Expression)。你发出同名目繁多标准测试,都获得相同结果。将这些测试合并为一个尺度表达式,并拿之规格表达式提炼成为一个独门函数。
  3. 合并重复的标准化有(Consolidate Duplicate Conditional
    Fragments)。在法表达式的每个分支上有同样的一样截代码。将即刻段还的代码搬移到规则表达式之外。
  4. 移除控制标记(Remove Control
    Flag)。在平等多重布尔表达式中,某个变量带有“控制标记”的意。以break语句或return语句取代控制标记。
  5. 因为卫语句取代嵌套条件表达式(Replace nested Conditional with
    Guard
    Clauses)。函数中之条件逻辑使人口难以看清正常的施行路径。使用卫语句表现有所的奇特状况。
  6. 以多态取代条件表达式(Replace Conditional with
    Polymorphism)。你眼前有只原则表达式,它根据目标类型的差选项不同的所作所为。将是规格表达式的每个分支放上一个子类内的覆写函数中,然后用原始函数声明也架空函数。
  7. 引入Null对象(Introduce Null
    Object)。你用数检查有目标是不是为null。将null值替换为null对象。
  8. 引入断言(Introduce
    Assertion)。某同截代码需要针对先后状态做出某种假设。以断言明确表现这种假设。

十一、简化函数调用

  1. 函数改名(Rename
    Method)。函数的名目不能揭示函数的用。修改函数的称号。
  2. 增长参数(Add
    Parameter)。某个函数需要打调用端得到重新多信息。为之函数添加一个目标参数,让该目标带来上函数所急需信息。
  3. 移除参数(Remove
    Parameter)。函数本体不再要某个参数。将拖欠参数去除。
  4. 将查询函数和改动函数分离(Separate Query from
    Modifier)。某个函数既归对象状态值,又涂改对象状态。建立两单例外的函数,其中一个顶查询,另一个顶修改。
  5. 令函数携带参数(Parameterize
    Method)。若干函数做了近乎之做事,但当函数本体中也饱含了不同的价值。建立单一函数,以参数表达那些不同的价。
  6. 坐显著函数取代参数(Replace Parameter with Explicit
    Methods)。你发出一个函数,其中完全取决于参数值而采用不同行为。针对该参数的每一个也许价值,建立一个单身函数。
  7. 保障对象完整(Preserve Whole
    Object)。你自某对象中取出若干值,将其当有平赖函数调用时之参数。改吗传送整个对象。
  8. 盖函数取代参数(Replace Parameter with
    Methods)。对象调用某个函数,并将所得结果作为参数,传递让另外一个函数。而接受该参数的函数本身也克调用前一个函数。让参数接受者去除该项参数,并一直调用前一个函数。
  9. 引入参数对象(Introduce Parameter
    Object)。某些参数总是充分当然地而起。以一个靶取代这些参数。
  10. 移除设值函数(Remove Setting
    Method)。类吃的某某字段应该于对象创建时叫设值,然后就不再改变。去丢该字段的有着设值函数。
  11. 隐藏函数(Hide
    Method)。有一个函数,从来不曾给另外任何类用到。将是函数修改也private。
  12. 坐工厂函数取代构造函数(Replace Constructor with Factory
    Method)。你指望当创建对象时不仅是开简单的构建动作。将构建函数替换为工厂函数。
  13. 打包向下转型(Encapsulate
    Downcast)。某个函数返回的靶子,需要由函数调用者实施向下转型。将朝下转型动作变到函数中。
  14. 为充分取代错误码(Replace Error Code with
    Exception)。某个函数返回一个一定的代码,用以代表某种错误情况。改用异常。
  15. 因测试取代异常(Replace Exception with
    Test)。面对一个调用者可以事先检查的尺度,你抛来了一个万分。修改调用者,使它们在调用函数之前先举行检讨。

十二、处理包括关系

  1. 字段上换(Pull Up
    Field)。两个子类拥有相同的字段。将该字段移至超类。
  2. 函数上换(Pull Up
    Method)。有些函数,在相继子类中出完全相同的结果。将该函数易至超类。
  3. 构造函数本体上换(Pull Up Constructor
    Body)。你在挨家挨户子类中所有一些构造函数,他们之本体几乎完全一致。在超类中初构筑一个构造函数,并当子类构造函数中调用它们。
  4. 函数下更换(Push Down
    Method)。超类中之之一函数只及一些(而休全部)子类有关。将这函数移到有关的那些子类去。
  5. 字段下更换(Push Down
    Field)。超类中之之一字段只为部分(而不全部)子类用到。将这字段移到用其的那些子类去。
  6. 提炼子类(Extract
    Subclass)。类中之一点特点只让一些(而非全部)实例用到。新建一个子类,将方面所说的那么部分特性移到子类中。
  7. 提炼超类(Extract
    Superclass)。两个像样产生相似特性。为当下简单只类似建立一个超类,将同样特性移至超类。
  8. 提炼接口(Extract
    Interface)。若干客户利用类接口中之同一子集,或者少独八九不离十的接口有有同样。将同样之子集提炼到一个独立接口中。
  9. 折叠继承体系(Collapse
    Hierarchy)。超类和子类之间无极端可怜差距。将它们合为一体。
  10. 培养模板函数(Form TemPlate
    Method)。你来一对子类,其中相应的少数函数以平等顺序执行类似之操作,但顺序操作的细节上拥有不同。将这些操作分别放上独立函数中,并保障它都生同样之签署,于是原函数为不怕易得一样了。然后将本函数上更换至超类。
  11. 因信托代表继承(Replace Inheritance with
    Delegation)。某个子类只利用超类接口中的一致部分,或是根本不欲持续而来之数码。在子类中新盖一个字段用以保存超类;调整子类函数令它改变而委托超类;然后去丢两者之间的接续关系。
  12. 因为延续取代委托(Replace Delegation with
    Inheritance)。你于有限独八九不离十中以委托关系,并经常也一体接口编写许多最简的嘱托函数。让委托类来就承受托类。

十三、大型重构

  1. 梳理并说继承体系(Tease Apart
    Inheritance)。某个继承体系以承担两件责任。建立两个连续体系,并经信托关系为其中一个可以调用另一个。
  2. 用过程化设计中转为对象设计(Convert Procedural Design to
    Objects)。你眼前有有风俗过程化风格的代码。将数据记录变成对象,将大块的行事分成小片,并以表现易入有关对象中。
  3. 用世界与发表/显示分离(Separate Domain from
    Presentation)。某些GUI类之中包含了世界逻辑。将世界逻辑分离出来,为她建立独立的世界接近。
  4. 提炼继承体系(Extract
    Hierarchy)。你发出有位做了极端多干活儿,其中有的做事是为恢宏法表达式完成的。建立继续体系,以一个子类表示一致栽奇特状况。

十四、经典句子

  1. Any fool can write code that a computer can understand. Good
    programmers write code that humans can understand.
    ——Martin
    Fowler

    旁一个白痴都能够写来计算机可以了解的代码。唯有写起人类容易懂的代码,才是良好之代码。
    ——Martin Fowler
  2. I’m not a great programmer; I’m just a good programmer with
    great habits.
    ——Kent Beck
    自我不是只英雄之程序员,我只是一个颇具一些妙习惯的好程序员。
    ——Kent Beck
  3. Computer Science is the discipline that believes all problems
    can be solved with one more layer of indirection.
    ——Dennis
    DeBruler

    计算机是是这么平等派系科学:它相信有题目还可以透过加一个间接层来化解。——Dennis
    DeBruler

自我的感受:

  1. 读者最有得的型更,或者了解了一些代码风格、设计模式、代码设计哲学等。
  2. 眼看是同样按以java语言来讲重构的题,读者最好明面向对象的基本知识,了解java、C++等面向对象的言语。
  3. 自家未是作java开发的,但是我了解面向对象,了解C++,java。书被的组成部分辩护和点在另设计语言上吧产生特别特别的拉,并且我发觉,在列面临我这儿尽管在运用在有些重构方法。书中那些过于结合面向对象的包装、继承、多态等文化之重构方法,可能就不过适用于面向对象的语言吧。
  4. **你值得一念。反复看,反复读。 **

自之胡说八道:

  1. 吃你们发现了,其实自己吧是一个程序员,还是低档程序员。
  2. 立即本书据说是java进阶必看,也是程序员必看之藏图书。设计语言本身可能有分别,造成部分特色也会发分,但是一些思想还是相通的。所有,非java、C++语言工作者,也一定要读一下。
  3. 传说stack
    overflow.com上载之一模一样篇《哪一样本书最有影响力,是每个程序员都应有读之?》。《重构》这按照开就是行前十,貌似是第六曰。

重构——改变既来代码的设计.jpg


ps:我的笔记只记录了理论部分,书上每种重构方法都配有实例代码来讲解,大家可以细细研究。