处理继承关系
函数上移
- 顾名思义,将子类逻辑重复的函数,移动到父类里。
字段上移
- 也是顾名思义的,要注意新字段需要对所有子类可见。
构造函数本体上移
- 将子类构造函数共有的行为上移至超类。需要考虑到超类和子类的构造函数调用顺序。
函数下移
- 如果超类中的某个函数只与一个(或少数几个)子类有关,那么最好将其从超类中挪走,放到真正关心它的子类中。
字段下移
- 同上
以子类取代类型码
- 构造函数中使用类型码,来实现不同逻辑。有些时候可以再多往前一步,引入子类。这里有两种处理方法:
- 让不同的类型称为子类,这种比较简单,但是类型逻辑就不能用在其他场合了,另外如果类型是可变的,也不能使用直接继承的方案。
- 在类中包含“类别属性”,从后者继承出多个子类型。
移除子类
- 随着软件的演化,子类所支持的变化可能会被搬移到别处,甚至完全去除,这时子类就失去了价值。子类存在着就有阅读成本,所以如果子类的用处太少,就不值得存在了。
提炼超类
- 如果两个类在做相似的事,可以利用基本的继承机制把它们的相似之处提炼到超类。另一种选择是提炼类。两种方案之间的选择,其实就是继承和委托之间的选择。提炼超类通常是比较简单的做法。
折叠继承体系
- 有时会发现一个类与其超类已经没有多大差别,不值得再作为独立的类存在。此时我就会把超类和子类合并起来。
以委托取代子类
- 本手法其实就是“组合优于继承”的意思。继承存在两个短板:一是继承只能处理一个维度上的多态,我们可能希望“人”的行为根据“年龄段”不同,并且根据“收入水平”不同。使用继承的话,子类可以是“年轻人”和“老人”,也可以是“富人”和“穷人”,但不能在两个维度上继承。第二个更大的问题是,继承引入了超类和子类的强耦合。在超类上做任何修改,都很可能破坏子类。每当修改超类的时候,都必须充分理解子类如何从超类派生。
- 上述的两个问题都能用委托解决,也就是组合,也可以理解为状态模式、策略模式或者组件模式。对于不同变化原因,我可以委托给不同的类。
- 组合优于继承,这句话更确切的表述应该是审慎地组合使用对象组合与类继承,优于单独使用其中任何一种。
以委托取代超类
- 一个经典的误用继承的例子:让栈继承列表。这个想法的出发点是想复用列表类的数据存储和操作能力,虽说复用是一件好事,但是同时列表类的多有操作,都会出现在栈类的接口上。然而一大部分操作对栈来说不适用。更好的做法应该是把列表作为栈的字段,把必要的操作委派给列表就行了。