12. Lazy Class(冗赘类)
你所创建的每一个class,都得有人去理解它、维护它,这些工作都是要花钱的。如果一个class的所得不值其身份,它就应该消失。项目中经常会出现这样的情况:某个class原本对得起自己的身份,但重檐使它身形缩水,不再做那么多工作;或开发者事前规划了某些变化,并添加一个class来就会这些变化,但变化实际上没有发生。不论上述哪一种原因,请让这个class庄严赴义吧。如果某些subclass没有做满足够工作,试试Collapse Hierarchy。对于几乎没用的组件,你应该以Inline Class对付它们。
个人理解:
冗余类 :存在需要耗费资源,没有意义的存在就是浪费
--解决方法:delete
13. Speculative Generality(夸夸其谈未来性)
这个令我们十分敏感的坏味道,命名者是Brian Foote。当有人说“噢,我想我们总有一天需要做这事”并因而企图以各式各样的挂勾和特殊情况来处理一些非必要的事情,这种坏味道就出现了。那么做的结果往往造成系统更难理解和维护。如果所有装置都会被用到,那就值得那么做;如果用不到,就不值得。用不上的装置只会挡你的路,所以,把它搬弄吧。
如果你的某个abstract class其实没有太大作用,请运用Collapse Hierarchy。非必要之delegation可运用Inline Class除掉。如果函数的某些参数示被用上,可对它实施Rename Method让它现实一些。
如果函数或class的惟一用户是test cases,这就飘出了坏味道Speculative Generality。如果你发现这样的函数或class,请把它们连同其test cases都删掉。但如果它们的用途是帮助test cases检测正当功能,当然必须刀下留人。
个人理解:
夸夸其谈未来型 :存在需要耗费资源,没有意义的存在就是浪费,你许诺的再斑斓的未来,不如一个干净的现在
--解决方法:delete
--有些可能是测试用的,测试完了就英勇就义吧~过河拆桥,不对,是卸磨杀驴
14. Temporary Field(令人迷惑的暂时值域)
有时你会看到这样的对象:其内某个instance 变量仅为某种特定情势而设。这样的代码让人不易理解,因为你通常认为对象在所有时候都需要它的所有变量。在变量未被使用的情况下猜测当初其设置目的,会让你发疯。
请使用Extract Class给这个可怜的孤独创造一个家,然后把所有和这个变量相关的代码都放进这个新家。也许你还可以使用Introduce Null Object在[变量不合法]的情况下创建一个Null对象,从而避免写出[条件式代码]。
如果class中有一个复杂算法,需要好几个变量,往往就可能导致坏味道Temporary Field的出现。由于实现者不希望传递一长串参数,所以他把这些参数都放进值域中。但是这些值域只在使用该算法时才有效,其它情况下只会让人迷惑。这时候你可以利用Extract Class把这些变量和其相关函数提炼到一个独立class中。提炼后的新对象将是一个method object。
个人理解:
令人迷惑的暂时值域 :资源放错了位置就是垃圾
--解决方法1:delete
--解决方法2:new class,method object;introduce null object。拎它回家,好好管教自家熊孩子不要乱跑。
15. Message Chains(过度耦合的消息链)
如果你看到用户向一个对象索求另一个对象,然后再向后者索求另一个对象,然后再索求另一个对象……这就是Message Chain。实际代码中你看到的可能是一长串getThis()或一长串临时变量。采取这种方式,意味客户将与查找过程中的航行结构紧密耦合。一旦对象间的关系发生任何变化,客户端就不得不做出相应修改。
这时候你应该使用Hide Delegate。你可以在Message Chain的不同位置进行这种重构手法。理论上你可以重构Message Chain上的任何一个对象,但这么做往往会把所有中介对象都变成Middle Man。通常更好的选择是:先观察Message Chain最终得到的对象是用来干什么的,看看能否以Extract Method把使用该对象的代码提炼到一个独立函数中,再运用Move Method把这个函数推入Message Chain。如果这条链上的某个对象有多位客户打算航行此航线的剩余部分,就加一个函数来做这件事。
有些人把任何函数链都视为坏东西,我们不这样想。呵呵,我们的总代表镇定是出了名的,起码在这件事情上是这样。
个人理解:
过度耦合的消息链: 这里说的是过度。sevlet的FilterChain应该不算。
--解决方法1:断开链,hide delegate。 http://blog.youkuaiyun.com/bendan999999999/article/details/3741178
--解决方法2:不要紧张,未必是坏的。
16. Middle Man(中间转手人)
对象的基本特征之一就是封装——对外部世界隐藏其内部细节。封装往往伴随delegation。比如说你问主管是否有时间参加一个会议,他就把这个消息委托给他的记事簿,然后才能回答你。很好,你没必要知道这位主管到底使用传统记事簿或电子记事簿抑或秘书来记录自己的约会。
但是人们可能过度运用delegation。你也许会看到某个class接口有一半的函数都委托给其它class,这样就是过度运用。这里你应该使用Remove Middle Man,直接和负责对象打交道。如果这样[不干实事]的函数只有少数几个,可以运用Inline Method把它们”inlining”,放进调用端。如果这些Middle Man还有其它行为内销可以运用Replace Delegation with Inheritance把它变成负责对象的subclass,这样你既可以扩展原对象的行为,又不必负担那么多的委托动作。
17. Inappropriate Intimacy(狎昵关系)
有时候你会看到两个classes过于亲密,花费太多时间去探究彼此的private成分。如果这发生在两个[人]之间,我们不必做卫道之士;但对于classes,我们希望它们严守清规。
就像古代恋人一样,过份狎昵的classes必须拆散。你可以采用Move Method和Move Field帮它们划清界线,从而减少狎昵行径。你也可以看看是否运用Change Bidirectional Association to Unidirectional让其中一个class对另一个斩断情丝。如果两个classes实在情投意合,可以运用Extract Class把两者共同点提炼到一个安全地点,让它们坦荡地使用这个新class。或者也可以尝试运用Hide Delegate让另一个class来为它们传递相思情。
继承往往造成过度亲密,因为subclass对superclass的了解总是超过superclass的主观愿望。如果你觉得该让这个孩子独自生活了,请运用Replace Inheritance with Delegation让它离开继承体系。
18. Alternative Classes with Different Interfaces(异曲同工的类)
如果两个函数做同一件事,却有着不同的签名式,请运用Rename Method根据它们的用途重新命名。但这往往不够,请反复运用Move Method将某些行为移入classes,直到两者的协议一致为止。如果你必须重复而赘余地移入代码才能完成这些,或许可运用Extract Superclass为自己赎点罪。
19. Incomplete Library Class(不完美的程序库类)
复用常被视为对象的终极目的。我们认为这实在是过度估计了。但是无可否认,许多编程技术都建立在library classes的基础上,没人敢说是不是我们都把排序算法忘得一干二净了。
Library classes构筑者没有未卜先知的能力,我们不能因此责怪他们。毕竟我们自己也几乎总是在系统快要构筑完成的时候才能弄清楚它的设计,所以library构筑者的任务真的很艰巨。麻烦的是library的形式往往不够好,往往不可能让我们修改其中的classes使它完成我们希望完成的工作。这是否意味那些经过实践检验的战术如Move Method等等,如今都派不上用场了?
幸好我们有两个专门就会这种情况的工具。如果你只想修改library classes内的一两个函数,可以运用Introduce Foreign Method;如果想要添加一大堆额外行为,就得运用Introduce Local Extension。
个人理解:
不完美的程序类库 :不要总想着复用,有人造好了轮子可以省时省力,但未必完全适合。
--困扰:类库很多都是final的吧,不能修改。关于final及final实现机制:http://www.importnew.com/7553.html http://www.importnew.com/7535.html
http://www.xuebuyuan.com/1258964.html(这个是c++,找到了)
--解决方法:添加函数introuduce foreign method,添加一堆行为 introduce local extension
http://blog.youkuaiyun.com/bendan999999999/article/details/3757403(看不懂了,没有写过这个⊙▂⊙)
20. Data Class(纯稚的数据类)
所谓Data Class是指:它们拥有一些值域,以及用于访问这些值域的函数,除此之外一无长物。这样的classes只是一种[不会说话的数据容器],它们几乎一定被其它classes过份细琐地操控着。这些classes早期可能拥有public值域,果真如此你应该在别人注意到它们之前,立刻运用Encapsulate Field将它们封装起来。如果这些classes内含容器类的值域,你应该检查它们是不是得到了恰当的封装;如果没有,就运用Encapsulate Collection把它们封装起来。对于那些不该被其它classes修改的值域,请运用Remove Setting Method。
然后,找出这些[取值/设值]函数被其它classes运用的地点。尝试以Move Method把那些调用行为搬移到Data Class来。如果无法搬移整个函数,就运用Extract Method产生一个可被搬移的函数。不久之后你就可以运用Hide Method把这些[取值/设值]函数隐藏起来了。
Data Class就像小孩子。作为一个起点很好,但若要让它们像[成年]的对象那样参与整个系统的工作,它们就必须承担一定责任。
个人理解:
幼稚地数据类:不会说话的数据容器。就像一个小孩子,若想像成年人一样参与工作就需要承担责任。(这是在说什么??)
--解决方法1:encapsulate field,encapsulate collection。(????)不该修改的就删除setter
21. Refused Bequest(被拒绝的遗赠)
Subclasses应该继承superclass的函数和数据。但如果它们不想或不需要继承,又该怎么办呢?它们得到所有礼物,却只从中挑选几样来玩!
按传统说法,这就意味继承体系设计错误。你需要为这个subclass新建一个兄弟,再运用Push Down Method和Push Down Field把所有用不到的函数下推给那兄弟。这样一来superclass就只持有所有subclasses共享的东西。常常你会听到这样的建议:所有superclasses都应该是抽象的。
既然使用[传统说法]这个略带贬义的词,你就可以猜到,我们不建议你这么做,起码不建议你每次都这么做。我们经常利用subclassing手法来复用一些行为,并发现这可以很好地应用于日常工作。这也是一种坏味道,我们不否认,但气味通常并不强烈。所以我们说:如果Refused Bequest引起困惑和问题,请遵循传统忠告。但不必认为你每次都得那么做。十有八九这种坏味道很淡,不值得理睬。
如果subclass复用了superclass的行为(实现),却又不愿意支持superclass的接口,Refused Bequest的坏味道就会变得浓烈。拒绝继承superclass的实现,这一点我们不介意;但如果拒绝继承superclass的接口,我们不以为然。不过即使你不愿意继承接口,也不要胡乱修改继承系,你应该运用Replace Inheritance with Delegation来达到目的。
个人理解:
被拒绝的遗赠: 继承没有设计好吧,子类不需要,父类又何必强制其实现呢
--解决方法1: 新建一个兄弟,push down' method, push down field
--解决方法2:接受传统说法:子类可以只使用父类功能的一部分。要是这么做就等着被骂吧~~哈哈
--解决方案3:拒担实现interface义务的话,replace inheritance with delegation 用委托代替继承 http://blog.youkuaiyun.com/yuwei629/article/details/8933392
22. Comments(过多的注释)
别担心,我们并不是说你不该写注释。从嗅觉上说,Comments不是一种坏味道;事实上它们还是一种香味呢。我们之所以要在这里提到Comments,因为人们常把它当作除臭剂来使用。常常会有这样的情况:你看到一段代码有着长长的注释,然后发现,这些注释之所以存在乃是因为代码很糟糕。这种情况的发生次数之多,实在令人吃惊。
Comments可以带我们找到本章先前提到的各种坏味道。找到坏味道后,我们首先应该以各种重构手法把坏味道去除。完成之后我们常常会发现:注释已经变得多余了,因为代码已经清楚说明了一切。
如果你需要注释来解释一块代码做了什么,试试Extract Method;如果你需要注释说明某些系统的需求规格,试试Introduce Assertion。
如果你不知道该做什么,这才是注释的良好运用时机。除了用来记述将来的打算之外,注释还可以用来标记你并无十足把握的区域。你可以在注释里写下自己[为什么做某某事]。这类信息可以帮助将来的修改者,尤其是那些健忘的家伙
个人理解:
过多的注释: 不知道做什么的时候才需要注释,如to-do
--解决方法1:哪里不确定才写注释
--解决方法2:健忘者的备注
--解决方案3:反正我就懒得写注释
整体总结:
略抽象,苦涩。作者热衷于面向对象。前半部分几乎在诱导你处处用封装,用继承。但后半部分又给你警钟:如果滥用的话又会怎样怎样。
总之吧,函数要实现单一功能,类的职责要专注,变量能封装就封装。类之间的耦合关系也要处理好,不要过度依赖,不要滥用继承,实时的使用委托(感觉这东西就是c++里的friend啊)。未雨绸缪未必是好事,用不着的该删删。