Skip to content

Refactoring // Improving the Design of Existing Code

Notifications You must be signed in to change notification settings

jfjbYXY/Refactoring

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

17 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Refactoring

Refactoring // Improving the Design of Existing Code

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

本库内代码为练习代码

参考书:《重构-改善既有代码的设计》-人民邮电出版社


#序 java行业的圣经:《重构》与《设计模式》

重构必须系统化进行;

重构的关键是:保持代码易读、易修改;

所谓重构是这样一个过程:在不改变代码外在行为的前提下,对代码做出修改,以改进程序的内部结构。本质上说就是在代码写好之后改进它的设计。

设计->编码->不断修改代码,这样的过程导致原先设计所得的系统整体结构逐渐衰弱。

#重构原则 开发中的两个行为:添加新功能、重构。添加新功能时不重构,重构时不添加新功能。

间接层的价值:允许逻辑共享;分开解释意图和实现;隔离变化;封装条件逻辑。(P61)

#提示 如果你发现自己需要为程序添加一个小特性,而代码结构使你无法很方便地达成目的,那就先重构那个程序,使特性的添加比较容易进行,然后再添加特性。

重构前,先检查自己是否有一套可靠的测试机制。这些测试必须有自我检验能力。

重构技术就是以微小的步伐修改程序。如果你犯下错误,很容易便可发现它。

任何一个傻瓜都能写出计算机可以理解的代码。唯有写出人类容易理解的代码,才是优秀的程序员。

当你感觉需要撰写注释时,请先尝试重构,试着让所有注释都变得多余。

##测试体系

  • 确保所有测试都完全自动化,让它们检查自己的测试结果。

  • 当需要添加特性的时候,先写相应的测试代码。

  • 面对无法自我测试的代码,重构之前首先必须改造这些代码,使其能够自我测试。一旦重构,可以更好的理解整个程序,从而找到更多bug。

  • Java中的测试惯用手法是每个类都有一个用于测试的main(),但不好操控,很难轻松运行多个测试。

  • 建立一个独立类用于测试,并在一个框架中运行,使测试工作更轻松。

  • 编写测试代码时,开始先让它失败,以证明测试机制的确可以运行,并且测试了它该测试的东西。

  • JUnit测试框架的用途是单元测试。

  • 测试的要诀是,测试你最担心出错的部分。

#代码的坏味道 - 促使你重构的迹象

##Duplicated Code(重复代码) 同一个类的两个函数含有相同的表达式;

兄弟子类含有相同表达式,提炼代码放入超类内;

不相关的类出现重复代码,放入一个独立类中或应该属于的某类中让另一个类调用。 ##Long Method(过长函数) 间接层——解释能力、共享能力、选择能力,都是由小函数支持的。 ##Large Class(过大的类) 将几个变量一起提炼到新类内。 ##Long Parameter List(过长参数列) 将来自同一对象的一对数据收集起来,并以该对象替换它们。如果缺乏对象归属,可以制造一个“参数对象”。 ##Divergent Change(分散式变化) 某个类经常因为不同的原因在不同的方向上发生变化。

找出这些特定原因造成的变化,将它们提炼到另一个类中。 ##Shotgun Surgery(散弹式修改) 如果每遇到某种变化,都必须在许多不同的类内做许多小修改。

应把一系列相关行为放进同一个类。

Divergent Change是指“一个类受多种变化影响”,Shotgun Surgery是指“一种变化引发多个类相应修改“。

这两种情况下都会希望整理代码,使”外界变化“与”需要修改的类“趋于一一对应。

##Feature Envy(依恋情结) 函数对某个类的兴趣高过对自己所处类的兴趣。那么,把它移到它该去的地方。

复杂情况:一个函数会用到几个类的功能。判断哪个类拥有最多被此函数使用的数据,然后把这个函数和那些数据摆在一起。将这个函数分解为数个较小函数并分别放置于不同地点。 ##Data Clumps(数据泥团) 两个类中相同的字段、许多函数签名中相同的参数。

将这些数据提炼到一个独立对象中,得到新对象后,就可以寻找Feature Envy,指出能够移至新类中的种种程序行为。 ##Primitive Obsession(基本类型偏执) 基本类型是构成结构类型的积木块。结构总是会带来一些额外开销。如果只为做一两件事而创建结构类型显得太麻烦。

可以编写一些与语言内置(基本)类型无异的小型类。 ##Switch Statements(switch惊悚现身) 少用switch case语句。switch的问题在于重复。同样的switch语句散布于不同地点,如果要为它添加一个新的case语句,就必须找到所有switch语句并修改它们。

大多数时候,一看到switch语句,就应该考虑以多态来替换它。 ##Parallel Inheritance Hierarchies(平行继承体系) shotgun surgery的特殊情况。当为某类增加一个子类,必须也为另一个类相应增加一个类。

让一个继承体系的实例引用另一个继承体系的实例。 ##Lazy Class(冗赘类) 某个类对不起自己的身价,没有足够的工作。

将它折叠继承或内联化。 ##Speculative Generality(夸夸其谈未来性) 某个抽象类其实没有太大用;函数的某些参数未被用上;函数或类的唯一用户是测试用例。

那么删掉它们。 ##Temporary Field(令人迷惑的暂时字段) 对象内某个实例变量仅为某种特定情况而设。

提取类把所有和这个变量相关的代码都放进这个类。

复杂情况:如果类中有一个复杂算法,需要好几个变量,而这些变量只在使用此算法时才有效。

把这些变量和其相关函数提炼到一个独立类中,提炼后的新对象将是一个函数对象。 ##Message Chains(过渡耦合的消息链) 隐藏委托关系。 ##Middle Man(中间人) 某个类接口有一半的函数都委托给其他类,这就是过渡运用。

移除中间人,直接和真正负责的对象打交道。 ##Inappropriate Intimacy(狎昵关系) 两个类过于亲密,花费太多时间去探究彼此的private成分。

搬移函数、搬移字段、双向关联改为单向关联、提取出新类、隐藏委托关系、以委托取代继承。 ##Alternative Classes with Different Interfaces(异曲同工的类) 两个函数做同一件事。

搬移函数、提炼超类。 ##Incomplete Library Class(不完美的库类) 如果想修改库类的一两个函数,引入外加函数;如果想添加一大堆额外行为,引入本地扩展。 ##Data Class(纯稚的数据类) 它们拥有一些字段,以及访问/读写这些字段的函数,除此之外一无长物。

将这些字段、函数封装起来,类似于Java Bean。

对于那些不该被其他类修改的字段,应移除设置函数。 ##Refused Bequest(被拒绝的遗赠) 子类不想或不需要继承超类的函数和数据。

意味着继承体系设计错误,函数下移、字段下移,让超类只持有所有子类共享的东西。

如果子类复用了超类的行为,却不愿意支持超类的接口,以委托取代继承。 ##Comments(过多的注释) 注释的存在或许说明代码很糟糕,注释可以带我们找到前面所说的各种坏味道,再以各种重构手段去除。

在重构之后,注释已经变得多余了,因为代码已经清楚说明了一切。

如果你不知道该做什么,这才是注释的良好运用时机。

About

Refactoring // Improving the Design of Existing Code

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages