
写在前面
重构起源于smalltalk,发扬于java和C#,它们都有成熟的重构工具。有一种说法是,《重构》和设计模式是java行业的圣经。我我的以为,重构就像修缮忒休斯之船同样,只是咱们是将船上的木板所有替换成了钢板。一个程序员若是看本身一年前写的代码而没有重构的念头,那么这个程序员可能这一年没有什么进步,固然也可能这块代码已经不须要重构了,但我想这种几率挺低的。java
由于各类缘由,没有人能在框架设计一开始就能套用设计模式写好,因此咱们的代码须要不断的重构。Gof的设计模式就是重构的目标。可是重构也是必须有理论准备的,必须系统化的进行,不然可能引入不可察觉的错误,风险更大。程序员
本文记录的重点只在于指出代码里的坏味道,若是在你的代码中“闻到”了这些坏味道就说明这块的代码可能须要重构了,至于怎么重构,书中有一套系统的方法,请期待后续的文章。设计模式
最后,但愿重构能变成像空气和水同样普通的技术。session
代码的坏味道有如厨房的油污,开始时不会以为有多大的影响,但时间长了就会累积成“恶心”又难以“清除”的污渍。咱们须要保持天天的清扫,而不是按期的“大扫除”。上面的“味道”就是一点一点的“油星”溅在“厨房”里,看到它们就顺手擦掉吧!
1、重构原则
1.重构的定义
- 重构(名词):对软件内部结构的一种调整,目的是在不改变软件可观察行为的前提下,提升其可理解性,下降其修改为本。
- 重构(动词):使用一系列重构手法,在不改变软件可观察行为的前提下,调整其结构。
-
关于重构须要强调的两点:app
- 重构的目的是使软件更容易被理解和修改。
- 重构不会改变软件可观察的行为——重构以后软件功能一如以往。
2.重构的目标
3.什么时候不应重构
- 现有代码根本不能正常运行时,应该重写,而不该重构。
- 若是项目已近最后期限,你也应该避免重构。
2、代码的坏味道
-
Duplicate Code(重复代码)函数
- 若是你在一个以上的地点看到相同的程序结构,那么能够确定,设法将它们合二为一,程序会变得更好。
-
Long Method(过长函数)工具
- 拥有短函数的对象会活的比较好,比较长。
- 咱们遵循这样一条原则:每当感受须要以注释来讲明点什么的时候,咱们就把须要说明的东西写进一个独立函数中,并以其用途(而非实现手法)命名。
- 条件表达式和循环经常也是提炼的信号。
-
Large Class(过大的类)测试
- 若是想利用单个类作太多事情,其内每每就会出现太多实例变量。
- 和“太多实例变量”同样,类内若是有太多代码,也是代码重复、混乱并最终走向死亡的源头。最简单的解决方案是把多余的东西消弭于类内部。
- 这里有个技巧:先肯定客户端如何使用它们(指“太多的实例变量”),而后运用Extract Interface为每一种使用方式提炼出一个接口。这或许能够帮助你看清楚如何分解这个类。
-
Long Parameter List(过长参数列)编码
- 若是你手上没有所需的东西,总能够叫另外一个对象给你。所以,有了对象,你就没必要把函数须要的全部东西都以参数传递给它了,只需传给它足够的、让函数能从中得到本身须要的东西就好了。
-
Divergent Change(发散式变化)
- 指的是,若是一个类中引入一个新的变化,须要修改多个函数,则须要考虑将这个类一分为二。
- 针对某一外界变化的全部相应修改,都只应该发生在单一类中,而这个新类内的全部内容都应该反应此变化。
-
Shotgun Surgery(霰弹式修改)
- Divergent Change是指“一个类受多种变化的影响”,Shotgun Surgery则是指“一种变化引起多个类相应的修改”。这两种状况下你都会但愿整理代码,使“外界变化”与“须要修改的类”趋于一一对应。
-
Feature Envy(依恋情结)
- 函数对某个类的兴趣高过对本身所处类的兴趣。
- 判断哪一个类拥有最多被此函数使用的数据,而后就把这个函数和那些数据摆在一块儿。
- 最根本的原则是:将老是一块儿变化的东西放在一起。数据和引用这些数据的行为老是一块儿变化的,但也有例外。若是例外出现,咱们就搬移那些行为,保持变化值在一地发生。
-
Data Clumps(数据泥团)
- 你经常能够在不少地方看到相同的三四项数据:两个类中相同的字段、许多函数签名中相同的参数。这些老是绑在一块儿出现的数据真应该拥有属于它们本身的对象。
- 一个好的评判办法是:删除众多数据中的一项。这么作,其余数据有没有于是失去意义?若是它们再也不有意义,这就是个明确信号:你应该为它们产生一个新对象。
-
Primitive Obsession(基本类型偏执)
- 对象的一个极大的价值在于:它们模糊(甚至打破)了横亘于基本类型和体积较大的类之间的界限。你能够轻松编写一些与语言内置(基本)类型无异的小型类。
- 对象技术的新手一般不肯意在小任务上运用小对象——像是结合数值和币种的money类、由一个起始值和一个结束值组成的range类、电话号码或邮政编码等的特殊字符串。你能够运用Replace Data Value With Object将原来单独存在的数据值替换为对象,从而走出传统的洞窟,进入煊赫一时的对象世界。
-
Switch Statements(switch惊悚现身)
- 面向对象程序的一个最明显特征就是:少用switch(或case)语句。
- 大多数时候,一看到switch语句,你就应该考虑以多态来替换它。
- 若是你只是在单一函数中有些选择事例,且并不想改动它们,那么多态就有点杀鸡用牛刀了。
-
Parallel Inheritance Hierarchies(平行继承体系)
- 每当你为某个类增长一个子类,必须也为另外一个类相应增长一个子类。
- 消除这种重复性的通常策略是:让一个继承体系的实例引用另外一个继承体系的实例。
-
Lazy Class(冗赘类)
- 你所建立的每个类,都得有人去理解它,维护它,这些工做都是要花钱的,若是一个类的所得不值其身价,它就应该消失。
-
Speculative Generality(夸夸其谈将来性)
- 当有人说“嗷,我想咱们总有一天须要作这事”,并于是企图以各类各样的钩子和特殊状况来处理一些非必要的事情,这种坏味道就出现了。
- 若是全部装置都会被用到,那就值得那么作;若是用不到,就不值得。用不上的装置只会挡你的路,因此,把它搬开吧。
-
Temporary Field(使人迷惑的暂时字段)
- 有时你会看到这的对象:其内某个实例变量仅为某个特定状况而设。
- 请使用Extract Class给这个可怜的孤儿创造一个家,而后把全部和这个变量相关的代码都放进这个新家。
-
Message Chains(过分耦合的消息链)
- 若是你看到用户向一个对象请求另外一个对象,而后再向后者请求另外一个对象,而后再请求另外一个对象……这就是消息链。
- 先观察消息链最终获得的对象是用来干什么的,看看可否以Extract Method把使用该对象的代码提炼到一个独立函数中,再运用Move Method把这个函数推入消息链。若是这条链上的某个对象有多位客户打算航行此航线的剩余部分,就加一个函数来作这件事。
-
Middle Man(中间人)
- 人们可能过分运用委托。你也许会看到某个类接口有一半的函数都委托给其余类,这样就是过分运用。这时应该使用Remove Middle Man,直接和真正负责的对象打交道。
-
Inappropriate Intimacy(狎昵关系)
- 有时你会看到两个类过于亲密,花费太多时间去探究彼此的private成分。
- 就像古代恋人同样,过度狎昵的类必须拆散。你能够采用Move Method和Move Field帮它们划清界线,从而减小狎昵行径。
- 继承每每形成过分亲密,由于子类对超类的了解老是超事后者的主观愿望。
-
Alternative Classes With Different Interfaces(殊途同归的类)
- 若是两个函数作同一件事,却有着不一样的签名。请运用Rename Method根据它们的用途从新命名。但这每每不够,请反复运用Move Method将某些行为移入类,直到二者的协议一致为止。
-
Incomplete Library Class(不完美的类库)
- 麻烦的是库每每构造得不够好,并且每每不可能让咱们修改其中的类使它完成咱们但愿完成的工做。
- 若是你只想修改类库的一两个函数,能够运用Introduce Foreign Method;若是想要添加一大堆额外行为,就得运用Introduce Local Extension。
-
Data Class(纯稚的数据类)
- 所谓Data Class是指:它们拥有一些字段,以及用于访问(读写)这些字段的函数,除此以外一无长物。
- Data Class就行小孩子,做为一个起点很好,但若要让它们像成熟的对象那样参与整个系统的工做,它们就必须承担必定责任。
-
Refused Bequest(被拒绝的遗赠)
- 若是子类复用了超类的行为(实现),却又不肯意支持超类的接口,Refused Bequest的坏味道就会变得浓烈。拒绝继承超类的实现,这一点咱们不介意;但若是拒绝继承超类的接口,咱们不觉得然。不过即便你不肯意继承接口,也不要胡乱修改继承体系,应该运用Replace Inheritance With Delegation来达到目的。
-
Comments(过多的注释)
- 经常会有这样的状况:你看到一段代码有着长长的注释,而后发现,这些注释之因此存在乃是由于代码很糟糕。
- 若是你不知道该作什么,这才是注释的良好运用时机。
3、两句重要的话
- 重构的基本技巧——小步前进,频繁测试。
- 模式是你但愿到达的目标,重构则是到达之路。