1.6.2 多重继承
当我们把继承理解为族谱树时,会发现我们可能不单从父母之一那里继承了特征(我们通常同时继承了父亲和母亲的特征)。当陌生人对一位自豪的妈妈说她的儿子眼睛很像爸爸时,妈妈的回答可能是“对,但他的鼻子像我”。
面向对象设计同样可以实现这样的多重继承,允许子类从多个父类那里继承特征。在实践中,多重继承可能是一件棘手的事情,有些编程语言(尤其是Java)甚至严格禁止这样做。然而,多重继承也有它的用处。最常见的是,用于创建包含两组完全不同行为的对象。例如,设计一个对象用于连接扫描仪并将扫描的文件通过传真发送出去,这一对象可能继承自两个完全独立的scanner和faxer对象。
只要两个父类拥有完全不同的接口,子类同时继承这两个类就并没有什么坏处。但是如果两个类的接口有重叠,多重继承就可能造成混乱。扫描仪和传真并没有相同的接口,同时继承它们的功能并没有什么问题。举个相反的示例,有一个摩托车类拥有move方法,还有一个船类也拥有move方法。
我们先想要将它们合并为一个终极水陆两用车,当调用move方法时,生成的类如何知道要执行的操作呢?这需要在设计时详细解释(本书作者之一是一名住在船上的水手,他确实需要解决这个问题)。
Python有一个方法解析顺序(Method Resolution Order,MRO),可以帮我们确定优先调用哪个方法。使用MRO规则是简单的,但最好的办法是避免多重继承。虽然多重继承作为一种可以把不相关的特征整合在一起的混入(mixin)[7]技术有一定的帮助,但在很多情况下使用对象组合是更简单的选择。
继承是一个非常有力的扩展行为和功能的工具,也是与面向对象设计相比更早的编程方法最具进步性的地方。因此,它通常是面向对象程序员最早学会的工具。但是要注意不要手里拿着锤子就把螺丝钉也看作普通钉子。继承对严格的“是一个”关系是最优的解决方案,但是可能被滥用。程序员经常用继承来共享代码,即使两种对象之间可能只有很少的关联,而不是严格的“是一个”关系。虽然这不一定是坏的设计,但却是一个极好的机会去考虑为何要采用这样的设计,用别的关系或者设计模式是否会更合适。