首页 > 代码库 > 『重构--改善既有代码的设计』读书笔记----代码坏味道【3】

『重构--改善既有代码的设计』读书笔记----代码坏味道【3】

    星期六了,适当出去放松了下,回来继续我们重构的话题。今天是坏味道【3】了,很多朋友跟我私信,叫我把坏味道出完,再出手法。其实这是有道理的,很多时候,"发现"远比"怎么做"重要的多。就拿设计模式来讲,GoF里面的设计模式相信有很多人都了解过。具体的设计模式应该怎么实现啊相信有很多人都背的滚瓜烂熟,但问题的难点往往在于你应该什么时候用这个设计模式。重构也一样,手法步骤都是死的,关键在于应该发现什么时候应该重构。所以,我还是决定继续出坏味道,把坏味道全部出完我们再去学手法。好了,进入正题:

    【8】Data Clumps(数据泥团)

    数据项就像孩子一样,喜欢成群结对的呆在一块,就比如startPoint和endPoint来说,你可以在很多类看到类似的用户,同时出现start,end这种情况,在两个类中有相同的这种字段,许多函数中有相同的这种参数,可以在很多地方看到相同的三四项数据,其实对于这些耦合在一起的数据来说,你确确实实应该给他们一个属于他们自己的对象,这样有几点好处:

    1) 复用,没错,面向对象带来的魔力就是复用,当我们将这些数据项绑定到一起形成一个对象的时候,我们就不需要在不同的地方去强调他们的耦合关系,我们只需要简简单单的声明一个他们的对象就可以了。

    2) 让他们拥有属于他们自己的行为,让这些数据项拥有自己的对象的同时你也赋予了他们拥有自己行为的能力。就比如拿startPoint和endPoint来说,如果你声明一个他们自己的对象RangePoint,你就可以增加他们自己的行为,比如findPoint--来检测这个点是不是在他们两点之间。你这样就不需要在他们出现的不同地方重复声明这个方法的实现,你就可以真正意义上的做到复用。

    3) 简化参数列表,一旦将他们绑定到一个对象之后,你就可以缩短他们之前所在函数的参数列表,你通过Extract Class将他们抽离到别的对象之后,运用Introduce Parameter Object或Preserve Whole Object来缩短他们的参数列表长度。简化函数调用,就算你提炼出来的新类在函数中只用他的部分字段你也别太介意,只要以一个新对象可以代替两个或更多的字段你就赚到了。

    一个比较有用的做法就是删除这些数据项的一项,然后看剩下来的数据项如果脱离它的话还有没有意义。如果不再有意义,那么你的重构机会就真的来了。减少字段和参数的个数可以去除一些坏味道,但更重要的是你可以通过提炼出来的新类来查看是否存在Feature Envy,这个可以帮助你指出能够转移到新类的种种行为,这样所有的小类都拥有自己的行为之后,你可以让原来的宿主类更贱精简,让类与类之间的分工合作更加明确。

    【9】Primitive Obsession(基本类型偏执)

    像C++这种语言和别的语言一样,都拥有两种数据,一种是原子级别的基本类型,比如int,double等,一种是由这些自定义类型组成的类类型,比如QString,QLineEdit等,一开始学习面型对象的人包括我也一样,往往在大多数时候不愿意在一些小函数小任务上对那些需要提炼的几个小参数做对象化。比如数值和币种我们不会像到去建立一个Money类,上问上的startPoint和endPoint我们不会想到去建立一个RangePoint类等等。你可以使用Replace Data Value with Object将原本单独存在的数值替换为对象,进入对象世界,拥有上文提到的如此多的好处。如果你想要替换的数据值是类型码,并且它不影响行为,你就可以运用Replace Type Code with Class来进行重构。如果类型码参与了相关的条件表达式进行判断,你可运用Replace Type Code with Subclass或者Replace Type Code with State/Strategy。如果你拥有像第8点一样总是放在一起的数据泥团,你可以运用Extract Class。如果你在参数列表中看到基本类型数据,你可以试试Introduce Parameter Object,如果你发现你正在从数组中挑选数据,可以试试Replace Array with Object。

    【10】Switch Statements(switch惊悚现身)

    文中作者用了“惊悚”来形容swtich语句,的确,面向对象中压根就不需要存在swtich,多态给了比swtich更优雅的解法。swtich语句本身就代表了重复,你在需要做类型判断的时候你就需要写这一长串的swtich不说,当你要增加新的类型你就需要寻找这些所有的swtich,维护工作非常困难。大多数时候,当你一看到swtich语句,你就应该考虑用多态来优雅的解决。问题是这个多态应该出现在哪 ?swtich语句常常跟类型码有关,你所要做的就是寻找与这些类型码有关的函数或类。应该先用Extract Method把这个swtich语句提炼到一个独立函数中去,然后运用Move Method把它搬移到需要多态性的那个类里。接下来你需要考虑是否使用Replace Type Code wtih Subclass或者Replace Type Code with State/Strategy。一旦这样的继承结构完成之后,你就可以运用Replace Condiional with Polymorphism来优雅的进行解决了。

    当然,有的同学会说我只在一个很小的函数中需要做选择需要做swtich判断,那这个时候你就需要自己做衡量了,这些类型码以后会不会一直用到,这串swtich代码之后会不会在别的地方出现,如果你确定不会,那么多态就不需要。这个时候你可以运用Replace Parameter with Explicit Methods来进行重构。如果你的选择之一有null,你可以运用Introduce Null Object替换if判断并且优雅的解决。

    【11】 Parallel Inheritance Hierarchies(平行继承体系)

     Parallel Inheritance Hierarchies其实是Shotgun Surgery的特殊情况。意思就是当你为某一个类增加子类的同时你必须为别的类同时增加子类。有个简单办法可以判断,当你发现某个类的继承体系前缀和另外一个继承体系前者完全相同,你便闻到了这股坏味道。

    解决这个办法的一般策略就是让一个继承体系的实例去引用另外一个继承体系的实例。然后不断运用Move Method和Move Fiield到被引用端,你就可以将引用端的继承体系完全打破,做到被引用端单一的继承体系。

    【12】Lazy Class(冗赘类)

    虽然面向对象世界带给我们对象的魅力,但并不是类越多就越好。虽然加入间接层可以带来各种方便,但所有的类都是需要人能够去理解和维护的。对于那些实际作用不大的类,或者因为某些重构原因让它变得没有价值的时候,或开发者事前规划了某些类来应对变化,但实际上并没有发生这些变化。不论上述哪一种原因,就让这个类消失好了,这样减少你的工作量的同时也在减少别人的工作量,因为很有可能将来维护代码的人还是你自己。如果子类没有做足够的工作,可以运用Collapse Hierarchy来打破继承体系,对于几乎没有用的组件,你可以运用Inline Class来对付它们。

『重构--改善既有代码的设计』读书笔记----代码坏味道【3】