重构:改善既有代码的设计

概述

  • 作者信息:[美] Martin Fowler 著,熊节 译
  • 重构:在不改变软件可观察行为的前提下改善其内部结构。

第1章 重构,第一个案例

1.1 起点

1.2 重构的第一步

  • 为即将修改的代码建立一组可靠的测试环境

1.3 分解并重组statement()

  • 将switch语句块提炼到独立函数中
  • Java IDE,如Eclipse、Idea均已提供良好的重构支持。
  • 更改变量名称是非常值得的行为,好的代码应该清楚表达出自己的功能,变量名称是代码清晰的关键。IDE均已支持更改变量名称。
  • 绝大多数情况下,函数应该放在它所使用的数据的所属对象(类)内。

1.4 运用多态取代与价格相关的条件逻辑

  • 最好不要在一另一个对象的属性基础上运用switch语句。如果不得不使用,也应该在对象自己的数据上使用,而不是在别人的数据上使用。
  • 一部影片可以在生命周期内修改自己的分类,一个对象却不能再声明周期内修改自己所属的类,采用State模式可以解决这个问题。

1.5 结语

第2章 重构原则

2.1 何谓重构

  • 两顶帽子,使用重构技术开发软件时,你把自己的时间分配给两种截然不同的行为:添加新功能、重构。添加新功能时,不应该修改既有代码,只管添加新功能。重构时不能添加新功能,只管改进程序结构。

2.2 为何重构

  • 消除重复代码,这个动作的重要性在于方便未来的修改。
  • Kent Beck,我不是个伟大的程序员,我只是个有着一些优秀习惯的好程序员。

2.3 何时重构

  • 用UML示意图展现设计,并CRC卡展示软件情节

2.4 怎么对经理说

2.5 重构的难题

  • 《数据库重构》

2.6 重构与设计

  • 预先设计

2.7 重构与性能

2.8 重构起源何处

第3章 代码的坏味道

3.1 Duplicated Code(重复的代码)

3.2 Long Method(过长函数)

3.3 Large Class(过大类)

3.4 Long Parameter List(过长参数列)

3.5 Divergent Change(发散式变化)

3.6 Shortgun Surgery(霰弹式修改)

3.7 Feature Envy(依恋情结)

3.8 Data Clumps(数据泥团)

3.9 Primitive Obsession(基本型别偏执)

3.10 Switch Statements(switch惊悚现身)

3.11 Parallel Inheritance Hierarchies(平行继承体系)

3.12 Lazy Class(冗赘类)

3.13 Speculative Generality(夸夸其谈未来性)

3.14 Temporary Field(令人迷惑的暂时值域)

3.15 Message Chai (过度耦合的消息链)

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章 建立测试体系

4.1 自我测试码的价值

  • 类应该包含它们自己的测试代码

4.2 JUnit测试框架

  • 程序员的好朋友:单元测试

4.3 添加更多测试

第5章 重构名录

5.1 重构的记录格式

  • name,summary,motivation,mechanics,examples

5.2 寻找引用点

5.3 这些重构准则有多成熟

  • 模式和重构之间有着一种与生俱来的关系。模式是你希望达到的模板,重构则是到达之路。

第6章 重新组织你的函数

6.1 Extract Method(提炼函数)

  • 动机
    1,如果每个函数的粒度都很小,那么函数被复用的机会就更大
    2,这会使高层的函数读起来就像一系列注释
    3,如果函数都是细粒度,那么函数的覆写也会更容易些。
  • 回传值改名为result

6.2 Inline Method(将函数内联化)

6.3 Inline Temp(将临时变量内联化)

6.4 Replace Temp With Query(以查询取代临时变量)

6.5 Introduce Explaining Variable(引入解释性变量)

6.6 Split Temporary Variable(剖解临时变量)

  • 如果临时变量承担多个责任,它就应该被替换(分解)为多个临时变量,每个变量只承担一个责任。同一个临时变量承担两件不同的事情,会令代码阅读者糊涂。

6.7 Remove Assignments to Paramete (移除对参数的赋值动作)

  • Java只采用按值传递方式,在按值传递的情况下,对参数的任何修改,都不会对调用端造成任何影响。
  • 为参数加上关键字final,从而强制它遵循”不对参数赋值“这一惯例。
  • 把参数视为final,因此很少在参数列表中这样标示参数。

6.8 Replace Method with Method Object(以函数对象取代函数)

  • 一个大型函数,其中局部变量的使用无法采用Exract Method方法,将这个函数放进一个单独的对象中,如此一来局部变量就成了对象内的字段,然后可以在同一个对象中将这个大型函数分解为多个小型函数。

6.9 Substitute Algorithm(替换你的算法)

第7章 在对象之间移动特性

7.1 Move Method(搬移函数)

7.2 Move Field(搬移值域)

7.3 Extract Class(提炼类)

7.4 Inline Class(将类内联化)

7.5 Hide Delegate(隐藏「委托关系」)

7.6 Remove Middle Man(移除中间人)

7.7 Introduce Foreign Method(引入外加函数)

7.8 Introduce Local Exte ion(引入本地扩展)

第8章 重新组织你的数据

  • 对象应该直接访问其中的数据,抑或应该通过访问函数来访问,争论不止的问题,作者建议直接访问的方式。
  • 哑数据
  • 魔法数,带有特殊含义的常量值。

8.1 Self Encapsulate Field(自封装值域)

  • 在”字段访问方式“这个问题上,存在两种截然不同的观点:
    1,一派认为,在该变量定义所在的类内中,可以自由访问它;
    2,另一派认为,即使在这个类中,你也应该只使用访问函数间接访问。
    3,两派之间的争论可以说是如火如荼。
  • 小心对待”在构造函数中使用设置函数“的情况。一般来说,设置函数被认为应该在对象创建后才使用,所以初始化过程中的行为有可能与设置函数的行为不同。这种情况下,在构造函数中直接访问字段,要不就是单独另建一个初始化函数。

  • IntRange (int low, int high) {
    initialize (low, high);
    }
    private void initialize (int low, int high) {
    _low = low;
    _high = high;
    }

8.2 Replace Data Value with Object(以对象取代数据值)

8.3 Change Value to Reference(将实值对象改为引用对象)

8.4 Change Reference to Value(将引用对象改为实值对象)

8.5 Replace Array with Object(以对象取代数组)

8.6 Duplicate Observed Data(复制「被监视数据」)

8.7 Change Unidirectional Association to Bidirectional(将单向关联改为双向)

8.8 Change Bidirectional Association to Unidirectional(将双向关联改为单向)

8.9 Replace Magic Number with Symbolic Co tant (以符号常量/字面常量 取代魔法数)

8.10 Encapsulate Field(封装值域)

8.11 Encapsulate Collection(封装群集)

8.12 Replace Record with Data Class(以数据类取代记录)

8.13 Replace Type Code with Class(以类取代型别码)

8.14 Replace Type Code with Subclasses (以子类取代型别码)

8.15 Replace Type Code with State/Strategy (以State/Strategy取代型别码)

8.16 Replace Subclass with Fields(以值域取代子类)

第9章 简化条件表达式

  • 单一出口原则,遵守该原则将会导致逻辑表达式判断冗长。
  • 较之于过程化程序而言,面向对象程序的条件表达式通常比较少,这是因为很多条件行为都被多态机制处理掉了。

9.1 Decompose Conditional(分解条件式)

9.2 Co olidate Conditional Expression(合并条件式)

9.3 Co olidate Duplicate Conditional Fragments (合并重复的条件片段)

9.4 Remove Control Flag(移除控制标记)

9.5 Replace Nested Conditional with Guard Clauses (以卫语句取代嵌套条件式)

  • 卫语句(Guard Clauses),如果某个条件极其罕见,就应该单独检查该条件,并在该条件为真时立刻从函数中返回。这样的单独检查常常被称为”卫语句“。
  • 嵌套条件代码往往由那些深信”每个函数只能有一个出口的“的程序员写出。
  • 条件反转,常常可以将条件表达式反转,从而实现Replace Nested Conditional with Guard Clauses

9.6 Replace Conditional with Polymorphism(以多态取代条件式)

9.7 Introduce Null Object(引入Null对象)

9.8 Introduce Assertion(引入断言)

第10章 简化函数呼叫

10.1 Rename Method(重新命名函数)

  • 要想成为一个真正的编程高手,起名的水平至关重要。

10.2 Add Parameter(添加参数)

10.3 Remove Parameter(移除参数)

10.4 Separate Query from Modifier(将查询函数和修改函数分离)

10.5 Parameterize Method(令函数携带参数)

10.6 Replace Parameter with Explicit Methods(以明确函数取代参数)

10.7 Preserve Whole Object(保持对象完整)

10.8 Replace Parameter with Method(以函数取代参数)

10.9 Introduce Parameter Object(引入参数对象)

  • Data Clumps,数据泥团
  • 本项重构的价值在于缩短参数列

10.10 Remove Setting Method(移除设值函数)

10.11 Hide Method(隐藏你的函数)

10.12 Replace Co tructor with Factory Method(以工厂方法取代构造函数)

10.13 Encapsulate Downcast(封装「向下转型」动作)

  • 自从Java 5加入模板机制后,非向下转型不可的场合几乎绝迹。

10.14 Replace Error Code with Exception(以异常取代错误码)

10.15 Replace Exception with Test(以测试取代异常)

第11章 处理概括关系

11.1 Pull Up Field(值域上移)

11.2 Pull Up Method(函数上移)

11.3 Pull Up Co tructor Body(构造函数本体上移)

  • 无法再子类中继承超类构造函数

11.4 Push Down Method(函数下移)

11.5 Push Down Field(值域下移)

11.6 Extract Subclass(提炼子类)

  • 子类只能用以表现一组变化,如果希望一个类以几种不同的方式变化,就必须使用委托。

11.7 Extract Superclass(提炼超类)

11.8 Extract Interface(提炼接口)

11.9 Collapse Hierarchy(折叠继承体系)

11.10 Form Template Method(塑造模板函数)

11.11 Replace Inheritance with Delegation(以委托取代继承)

11.12 Replace Delegation with Inheritance(以继承取代委托)

第12章 大型重构

12.1 Tease Apart Inheritance(疏理并分解继承体系)

12.2 Convert Procedural Design to Objects(将过程化设计转化为对象设计)

12.3 Separate Domain from Presentation(将领域和表述/显示分离)

12.4 Extract Hierarchy(提炼继承体系)

  • 当遇到瑞士军刀般的类——不但能够开罐、砍小树枝、还能在演示会上打出激光强调重点,这时需要一个好策略(本项重构),将它的各个功能梳理并分开。

第13章 重构,复用,与现实

13.1 现实的检验

13.2 为什么开发者不愿意重构他们的程序

13.3 再论现实的检验

13.4 重构的资源和参考数据

13.5 从重构联想到软件复用和技术传播

13.6 结语

13.7 参考文献

第14章 重构工具

14.1 使用工具进行重构

14.2 重构工具的技术标准

14.3 重构工具的实用标准

14.4 小结

第15章 总结

参考书目

要点列表

索引

你可能感兴趣的:(java)