重构_改善既有的代码设计(三)

重构,须要培养出本身的判断力,学会判断一个类内有多少实例变量算是太大,一个函数内有多少行代码才算太长。数据库

一、重复代码设计模式

(1)最单纯的重复代码是“同一个类的两个函数含有相同的表达式”,只须要抽出相同的代码,两个地方都调用就能够了。若是是两个绝不相干的类出现重复代码,你应该考虑将代码抽到一个独立类当中。可是,重复代码所在的函数也可能的确只属于某个类,另一个类只能调用它。你必须决定这个函数放在哪儿最合适,并确保它被安置后就不会再在其余任何地方出现。session

(2)若是两个互为兄弟的子类内含相同表达式,须要提炼出代码推入超类内。若是代码只是类似,并不是彻底相同,那么就得分离类似部分和差别部分。(这里提到一个Template Method设计模式,补上)app

二、过长函数函数

程序越长越难理解。“间接层”所能带来的所有利益--解释能力、共享能力、选择能力都是由小型函数支持的。现代OO语言几乎已经彻底免除了进程内的函数调用开销。不过代码阅读者仍是得多费力气,由于他必须常常转换上下文去看看子程序作了什么。若是你能给函数起个好名字,读者就能够经过名字了解函数的做用,根本就没必要去看其中写了什么。条件表达式和循环经常是提炼的信号。你可使用Decompose Conditional(这应该是一种处理方法,分解条件?)处理条件表达式。至于循环,你应该将循环和其内的代码提炼到一个独立函数中。工具

三、过大的类设计

若是想利用单个类作太多事情,其内每每就会出现太多实例变量。能够讲几个变量一块儿提炼至新类内。提炼时应该选择类内彼此相关的变量,将它们放在一块儿。例如depositAmount和depositCurrency可能应该隶属同一个类。一般若是类内的数个变量有着相同的前缀或字尾,这就意味着有机会把它们提炼到某个组件内。若是这个组件适合做为一个子类,你会发现Extract Subclass每每比较简单。若是类内有太多代码,最简单的解决方案是把东西消弭于类内部,好比有五个“百行函数”,它们之中不少代码都相同,那么或许你能够把它们变成五个“十行函数”和十个提炼出的“双行函数”。若是你的Large Class是一个GUI类,你可能须要把数据和行为移到一个独立的领域对象去。你可能须要两边各保留一些重复数据,并保持两边同步。对象

四、过长的参数列表继承

过长的参数列表难以理解。函数所需的全部东西都以参数传递进去,除此以外只能选择全局数据,而全局数据是邪恶的东西。对象技术改变了这一状况:若是你手上没有所需的东西,总能够叫另外一个对象给你。此为对象参数。接口

五、Divergent Change(发散式变化)

咱们但愿软件可以更容易被修改。一旦须要修改,咱们但愿可以跳到系统的某一点,只在该处作修改。若是某个类常常由于不一样的缘由在不一样方向上发生变化,Divergent Change就出现了。好比:新加入一个数据库,必须修改三个函数;新出现一种金融工具,必须修改四个函数。那么此时也许将这个对象分红两个会更好,这样一来每一个对象就能够只因一种变化而须要修改。固然,每每在加入新数据库或新金融工具后,你才能发现这一点。

六、Shotgun Surgery(霰弹式修改)

若是每遇到某种变化,你都必须在许多不一样的类中作出许多小修改,你所面临的坏味道就是Shotgun Surgery。若是须要修改的代码三步四处,你不但很难找到它们,也很容易忘记某个重要的修改。这种状况下你应该把全部须要修改的代码放进同一个类,若是眼瞎没有合适的类能够安置这些代码,就创造一个。

七、Feature Envy(依恋情结)

对象技术的所有要点在于:这是一种“将数据和对数据的操做行为包装在一块儿”的技术。函数可能由于数据,对某个类的兴趣高过对本身所处类的兴趣。这时候须要把它移到该去的地方。若是复杂一点,一个函数每每会用到几个类的功能,那么咱们的原则是:判断哪一个类拥有最多被此函数使用的数据,而后就把这个函数和那些数据摆在一块儿。这里再次提到Strategy模式(复习),还有Visitor模式(补上)

八、Data Clumps(数据泥团)

你经常能够在不少地方看到相同的三四项数据:两个类中相同的字段、许多函数签名中相同的参数。这些老是绑在一块儿出现的数据真应该拥有属于它们本身的对象。可能在使用时,只用上了新对象的部分字段,可是只要以新对象取代两个(或更多)字段,你就值回票价了。获得新对象以后,你就能够着手寻找Feature Envy,这能够帮你指出可以移至新类中的种种程序行为。没必要过久,全部类都将在它们的小小社会中充分发挥价值。

九、Primitive Obsession(基本类型偏执)

大多数变成环境都有两种数据:结构类型容许你将数据组织成有意义的形式;基本类型则是构成结构类型的积木块。对象的一个极大的价值在于:它们模糊(甚至打破)了横亘于基本数据和体积较大的类之间的界限。你能够尝试在小任务上用小对象--像是结合数值和币种money类、有一个起始值和一个结束值组成的range类。将单独存在的数据值替换为对象,从而走出传统的窟窿,进入煊赫一时的对象世界。

十、Switch Statements(switch惊悚现身)

面向对象程序的一个最明显特征就是:少用switch(或case)语句。从本质上说,switch语句的问题在于重复。你常会发现一样的switch语句散布于不一样地点。若是要为它添加一个新的case子句,就必须找到全部switch语句并修改它们。面向对象的多态概念可为此带来优雅的解决办法。(具体事例在这本书的第一章)

十一、Parallel Inheritance Hierarchies(平行继承体系)

Parallel Inheritance Hierarchies实际上是Shotgun Surgery的特殊状况。在这种状况下,每当你为某个类增长一个子类,必须也为另外一个类相应增长一个子类。若是你发现某个继承体系的类名称前缀和另外一个继承体系的类名称前缀彻底相同,即是闻到了这种坏味道。消除这种重复性的通常策略是:让一个继承体系的实例引用另外一个继承体系的实例。

十二、Lazy Class(冗赘类)

你所建立的每个类,都得有人去理解它、维护它,这些工做都是要花钱的。若是一个类的所得不值其身价,它就应该消失。项目中常常会出现这样的状况,某个类本来对得起本身的身价,但重构使它身形缩水,再也不作那么多工做;或开发者事先规划了某些变化,并添加一个类来应付这些变化,但变化实际上没有发生。

1三、Speculative Generality(夸夸其谈将来性)

有些代码若是暂时用不到,就把它去掉。而不是说“我总有一天会用到”,这么作的结果每每形成系统更难理解和维护。

1四、Temporary Field(使人迷惑的暂时字段)

有时你会看到这样的对象:其内某个实例变量(就是非static修饰的变量,也被称为成员变量。对应的是类变量,static修饰的变量)仅为某种特定状况而设。这样的代码让人不易理解,由于你一般认为对象在全部时候都须要它的全部变量。在变量未被使用的状况下猜想当初其设置目的,会让你发疯的。把全部和这个变量相关的代码都放进一个新类。也许你还能够在变量不合法的状况下,建立一个null对象(空对象,应该是这个意思),从而避免写出条件式代码

1五、Message Chains(过渡耦合的消息链)

若是你看到用户向一个对象请求另外一个对象,而后再向后者请求另外一个对象,而后再请求另外一个对象。。。。。。这就是消息链。实际代码中你看到的多是一长串的getThis()或一长串临时变量。采用这种方式,意味客户代码将与查找过程当中的导航结构紧密耦合。一旦对象间的关系发生任何变化,客户端就不得不作出相应修改。你能够再消息链的不一样位置进行这种重构手法。理论上能够重构消息链上的任何一个对象,但这么作每每会把一系列对象都变成Middle Man。一般更好的选择是:先观察消息链最终获得的对象是用来干什么的,看看可否把使用该对象的代码提炼到一个独立函数中,而后把这个函数推入消息链。若是这条链上的某个对象有多位客户打算航行此航线的剩余部分,就加一个函数来作这件事。

1六、Middle Man(中间人)

对象的基本特征之一就是封装--对外部世界隐藏其内部细节。封装每每伴随委托。好比说你问主管是否有时间参加一个会议,他就把这个消息“委托”给他的记事簿,而后才能回答你。你没有必要知道这位主管到底使用传统记事簿或电子记事簿亦或秘书来记录本身的约会。可是人们可能过渡运用委托。你也许会看到某个类接口有一半的函数都委托给其余类,这样就是过渡运用。这时应该去掉中间人,直接和真正负责的对象打交道。若是这样“不干实事”的函数只有少数几个,能够把它们放进调用端。若是这些Middle Man还有其余行为,能够把它变成实则对象的子类,这样你既能够扩展原对象的行为,又没必要负担那么多的委托动做。

1七、Inappropriate Intimacy(狎昵关系)

有时你会看到两个类过于亲密,花费太多时间去探究彼此的private成分,你就须要帮它们划清界限,减小狎昵行径。若是它们有一些共同点,能够将共同点提炼到一个新类中。继承每每形成过分亲密,由于子类对超类的了解老是超事后者的主观愿望。若是你以为该让这个孩子独自生活了,可让它离开继承体系。

1八、Alternative Classes with Different Interfaces(殊途同归的类)

若是两个函数作同一件事,却有着不一样的签名(应该是函数名的意思),根据它们的用途从新命名。但这每每不够,请反复移动方法将某些行为移入类,直到二者的协议一致为止。(协议?)。若是你必须重复而赘余地移入代码才能完成这些,或许能够尝试抽出父类。

1九、Incomplete Library Class(不完美的库类)

所谓Data Class是指:它们拥有一些字段,以及用于访问(读写)这些字段的函数,除此以外一无长物。这样的类只是一种不会说话的数据容器,它们几乎必定被其余类过度细琐地操控着。这些类可能拥有public字段,注意封装。若是这些类内含容器类的字段,你应该检查他们是否是获得了恰当的封装。找出这些取值/设置函数被其余类运用的地点,尝试将那些调用行为搬移到Data Class来。若是没法搬移整个函数,就抽出一个可被搬移的函数。不久以后,你就能够把这些取值/设置函数隐藏起来了。Data Class就像小孩子。做为一个起点很好,但若要让它们像成熟的对象那样参与整个系统的工做,它们就必须承担必定责任。

20、Comments(过多的解释)

经常会有这样的状况:你看到一段代码有着经常的注释,而后发现,这些注释之因此存在乃是由于代码很糟糕。当你感受须要撰写注释时,请先尝试重构,试着让全部注释都变得多余。若是你不知道该作什么,这才是注释的良好运用时机。除了用来记述未来的打算以外,注释还能够用来标记你并没有十足把握的区域。你能够在注释里写下本身“为何作某某事”。这类信息能够帮助未来的修改者,尤为是那些健忘的家伙。


这一章主要讲述代码的坏味道,哪些地方可能须要重构,增长本身重构的判断力。

相关文章
相关标签/搜索