3.13 夸夸其谈未来性(Speculative Generality)
不结合需求的框架不是好框架。
如果说,在编写一个框架时,考虑所有所有的情况,那么这个框架必然是庞大的,有些东西是用户无法用得着的。(虽然可扩展性很好,但是没用到的,也不可能用到的就是废代码)
可扩展性往往体现在抽象类上,如果你发现,在当前需求下,这个抽象类没有太大作用,那么就运用折叠继承体系(11.9 Collapse Hierarchy)将抽象类与子类合二为一。
折叠继承体系(11.9 Collapse Hierarchy):目的是简化继承结构,减少继承带来的复杂性,去除多余的类。
如果一个比较小的类(字段少,方法少)频繁用于某个特定的类,那么这个类也许没有必要单独存在,就应该将类内联化(7.4 Inline Class)。
将类内联化(7.4 Inline Class):小类并入大类。这与提炼类(Extract Class)的做法相反。
可扩展性往往还体现在参数上,为了某种可能性,也许一些个方法上会额外附带有一些暂时用不上的参数,这个时候就该注意,如果暂时用不上的参数在很长一段时间内都用不上,那么就移除参数(10.3 Remove Parameter)吧。
移除参数(10.3 Remove Parameter):移除参数操作很简单,但是一定要在操作之前去检查这个参数是否有用。(多态的情况下更需要注意,防止编译失败)
可扩展性的方法容易起一些模糊的名称,这会使得理解代码成为问题,此时应该函数改名(10.1 Rename Method)。
函数改名(10.1 Rename Method):如果你看到一个函数名称不能很好地表达它的用途,就应该马上加以修改。
代码首先是给人看的,其次才是给计算机看。
如果函数或类的唯一用户是测试用例,这就飘出了夸夸其谈未来性的味道,这时就该把它们连同测试用例一并删掉。但如果它们的用途是帮助测试用例检测正当功能,务必刀下留人。
3.14 令人迷惑的临时字段(Temporary Field)
当你发现,在A类中存在有某些字段,这些字段仅仅会在某种特定情况下才会被使用,在没有被用的情况下,这些字段完全就是游离在A类之外的。此时就该考虑,这些用于某种场合的字段能否提炼类(7.3 Extract Class)。
提炼类(7.3 Extract Class):在重构过程中也许会出现这种情况——在一个类的内部发生了重构,里面的一些方法被分割成好几部分,此时,难免会有一些作用面较窄的小方法,这些方法看起来和本类毫无关联,这也许意味着,它们不属于这个类,也许需要找一个自己的家,也许需要自己造一个家。
同时,可能需要使用引入Null对象(9.7 Introduce Null Object)来避免条件表达式判断null。
引入Null对象(9.7 Introduce Null Object):按照书中9.7节的代码而言,是给Customer新建了一个子类NullCustomer,这个子类中一切方法的行为均提前设定(与原先为null时的行为内容相同)。
这个坏味道通常会出现在处理过长的参数列之后,因为处理参数列有时需要把一些参数作为临时字段,这些字段仅在使用这个算法时有效,其余时候会让人迷惑。此时,就考虑此节中的重构方法吧。
3.15 过度耦合的消息链(Message Chains)
消息链:A类对象调用B类对象,然后再用B类对象调用C类对象,然后再用C类对象调用D类对象……在代码中通常体现为
a.getB().getC().getD();
消息链的劣势在于,当对象间的关系发生变化,就会发生连锁反应。
有些时候,消息链是不可缺少的,但我们可以通过重构来降低消息链的耦合度。
隐藏委托关系(7.5 Hide Delegate)是一个好办法。
隐藏委托关系(7.5 Hide Delegate):A类获取B类对象调用B类的方法,相当于
a.getB().b()
,此时为了隐藏委托关系,可以直接在A类中写一个b()方法,然后在b()方法内部通过B类对象调用真正的方法。
隐藏委托关系的作用在于,减少消息链的长度,隐藏消息链中真正的实现逻辑。理论上可以重构消息链中任何一个对象,但这往往会把一系列对象都变成中间人(Middle Man)。
通常有一个更好的选择:先观察消息链的末尾,了解最终得到的对象的用途,然后尝试使用提炼函数(6.1 Extract Method),最后运用搬移函数(7.1 Move Method)把提炼出的函数推入消息链。