《重构---改善既有代码的设计》总结二之代码的坏味道

来源:互联网 发布:js url动态传参数 编辑:程序博客网 时间:2024/04/30 04:15

如果尿布臭了,就换掉它


一、Duplicated Code(重复代码)

坏味道行列中首当其冲的就是Duplicated Code.如果你在一个以上的地点看看到相同的程序结构,那么可以肯定:设法将它们合而为一,程序会变得更好。

同一个类有两个函数含有相同的表达式,提炼出重复的代码,然后让这两个地点都调用被提炼出来的那一段代码。

两个互为兄弟的子类含有相同的表达式,将两个类中重复的代码提炼出来,再推入超类。

如果两个毫不相关的类出现重复代码,应该考虑将其中的一个抽取到新类中,然后在另一个类中使用这个新类。


二、过长函数

拥有短函数的对象会活得比较好、比较长。

“间接层”所能带来的全部利益---解释能力、共享能力、选择能力---都是由小型函数支持的。

你应该更积极地分解函数。我们遵循这样一条原则:每当感觉要以注释来说明点什么的时候,我们就吧需要说明的东西写进一个独立函数中,并以其用途(而非实现手法)命名。

提炼哪一段代码的提示:1、寻找注释。它们通常能指出代码用途和实现手法之间的语义距离。2、条件表达式和循环。


三、Large Class(过大的类)

类内有太多实例变量,就将几个变量一起提炼至新类内。

类内如果有太多代码,也是代码重复、混乱并最终走向死亡的源头,最简单的是把多余的东西消弭于类内部,提炼重复,缩减函数大小。还可以,先确定客户端如何使用它们,然后为每一种使用方式提炼出一个接口。


四、Long Parameter List(过长参数列表)

把函数所需的所有东西都以参数传递进去。这可以理解,因为除此之外就只能选择全局数据,而全局数据是邪恶的东西。

如果你手上没有所需的东西,总可以叫另一个对象给你。因此可以传递一个让函数可以从中获得自己所需要东西的对象就可以了。

 

五、Divergent Change(发散式变化)

针对某一外界变化的所有相应修改,都只应该发生在单一类中,而这个新类中的所有内容都应该反应此变化。


六、Shotgun Surgery(霰【xian】弹式修改)

Shotgun Surgery类似Diergent Change,但恰恰相反。如果每遇到某种变化,你都必须在许多不同的类内做许多小修改,那么所面临的坏味道就是Shotgun Surgery.解决方法是把所有需要修改的代码放进同一个类。

Divergent Change是指“一个类收多种变化的影响”,Shotgun Surgery则是指“一种变化引发多个类相应修改”。这两种情况下你都会希望整理代码,使“外界变化”与“需要修改的类”趋于一一对应。


七、Feature Envy(依恋情结)

函数对某一个类的兴趣高过于对自己所处类的兴趣。方法:把这个函数移植到它该去的另一个地点。

函数往往有多个类的功能,该置于何处的原则是:判断哪个类拥有最多被此函数使用的数据,然后就把函数和那些数据摆在一起。

将总是一起变化的东西放在一块儿。


八、Data Clumps(数据泥团)

总是绑在一起出现的数据帧应该拥有属于它们自己的对象。

删掉众多数据中的一项,这么做,其它数据有没有因此失去意义。如果它们不再有意义,这就是个明确信号:你应该为它们产生一个新对象。

减少字段和参数个数,当然可以去除一些坏味道,但是更重要的是:一旦拥有新对象,你就有机会让程序散发出一种芳香。


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

结构类型数据允许你将数据组织成有意义的形式;基本类型则是构成结构类型的积木块。

对象的一个极大的价值在于:它们模糊(甚至打破)了横亘于基本数据和体积较大的类之间的界限。

将原本单独存在的数据值替换为对象。


十、Switch Statements(Switch 惊悚现身)

面向对象程序的一个最明显特征就是:少用switch(或case)语句。从本质上说,switch语句的问题在于重复。

面向对象中的多态概念可以为此带来优雅的解决办法。


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

Parallel Inheritance Hierachies其实是Shotgun Surgery的特殊情况。这种情况下,每当你为某个类增加一个子类,必须也为另一个类相应增加一个子类。消除这种重复的一般策略是:让一个集成体系的实例引用另一个集成体系的实例。


十二、Lazy Class(冗赘类)

如果一个类的所得不值其身价,它就应该消失。


十三、Speculative Generality(夸夸其谈未来性)

用不上的装置只会挡你的路,所以,把它搬开吧。

如果函数或类的唯一用户就是测试用例,把它们连同测试用例一并删掉。但如果它们的用途是帮助测试用例检查正当功能,当然必须刀下留人。


十四、Temporary Field(令人迷惑的暂时字段)

其内某个实例变量仅为某种特定情况而设。在变量未被使用的情况下猜测当初其设置的目的,会让你发疯的。

字段一直未被使用,只在使用该函数时才有效。将变量和与其相关的函数提炼到一个独立类中。提炼后的新对象将是一个函数对象。


十五、Message Chains(过度耦合的消息链)

如果你看到用户向一个对象请求另一个对象,然后再向另一个对象请求另一个对象,然后再请求另一个对象....这就是消息链。更好的选择是:先观察消息链最终得到的对象是用来干什么的,看看能否把使用该对象的代码提炼到一个独立的函数中,再把这个函数推入消息链。


十六、Middle Man(中间人)

对象的基本特征就是封装---对外部世界隐藏其内部细节。封装往往伴随委托。

某个类接口有一般的函数都委托给其它类,这样就是过度运用。这时应移除委托,直接和真正负责的对象打交道。


十七、Inappropriate Intimacy(狎【xia】昵关系)

两个类过于亲密,花费太多时间去探究彼此的private成分。这样过于狎昵的类必须拆散。可以通过移除方法和移除变量来划清界线。

继承往往造成过度亲密,因为子类对超类的了解总是超过后者的主观愿望。如果你觉得该让这个孩子独自生活了,就让它离开继承体系。


十八、Alternative Classes with Different Interfaces(异曲同工的类)

如果两个函数做着同一件事,却有着不同的签名,则根据他们的用途重新命名。并通过移除方法,将某些行为移入新类,知道两者的协议一致为止。


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

如果只想修改库类的一两个函数,可以运用引用新的方法;如果想要添加一大堆额外的行为,就得运用引入本地继承。


二十、Data Class(纯稚的数据类)

所谓Data Class是指:它们拥有一些字段,以及用于访问(读写)这些字段的函数,除此之外一无长物。这样的类只是一种不会说话的容器,它们几乎一定被其他类过分细琐地操控着。这些类可能拥有public字段,果真如此你应该在别人注意到它们之前,将字段封装起来。如果这些类内含容器类的字段,你应该检查它们是不是得到了恰当的封装;如果没有,它们通过集合封装起来。对于那些不该被其它类修改的字段,移除赋值器和取值器。


二十一、Refused Bequest(被拒绝的遗赠)

如果子类复用了超类的行为(实现),却又不愿意支持超类的接口,Refused Bequest的坏味道就会变得浓烈。拒绝继承超类的实现,这一点我们不介意;但如果拒绝继承超类的接口,我们不以为然。不过即使你不愿意继承接口,也不要胡乱修改继承体系。


二十二、Comments(过多注释)

如果代码已经清楚说明了一切,那么注释就已经变得多余了。

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

如果你还不知道该做什么,这才是注释的良好运用时机。除了用来记述将来的打算之外,注释还可以用来标记你并无十足把握的区域。你可以在注释里写下自己“为什么做某某事”,这类信息可以帮助将来的修改者,尤其是那些健忘的家伙。


0 0
原创粉丝点击