继承机制

背景

本文为《冒号课堂:编程范式与OOP思想》一书中第九课的思考与总结。

问题与解答

请总结3类继承各自的特点和适用场合。

3类继承:只继承实现,只继承接口(接口继承),既继承实现又继承接口(类继承)。

只继承实现:C++可以实现,非公有继承可以看作是一种匿名的非显性合成,可以访问基类的protected成员,也可以覆盖(override)基类的方法。

接口继承:生产可重用的新代码。

类继承(又叫实现继承):消费可重用的旧代码。

里氏代换原则的重要意义何在?

里氏代换原则(Liskov Substitution Principle):类型A的子类型B应该满足条件:将程序中类型A的对象置换为类型B的对象,不会影响程序的合理性和正确性。里氏代换原则的本质正是为了保证规范抽象。

实现继承也应该遵循里氏代换原则,这既是一种义务,也是一种权益。说是义务,是因为实现继承在继承实现的同时也继承了接口,按理也应继承接口的规范;说是权益,是因为接口的继承能让代码被重用。如果只是为了重用基类的代码,并不希望重用它的接口,那就应该采用合成而不是类继承的方式。这涉及一个通用的编程原则,即尽可能地弥合语法与语义之间的缝隙,以压缩代码臭虫的生存空间。

数据抽象与多态抽象是OOP中最重要的两种抽象,它们分别在不同层次上完成了对接口与实现的分离。把握这两条主线对培养OOP的语感至关重要,请平时多加揣摩和实践。

所谓多态,意指一种类型可能具备多种类型的形式。显然,多态抽象是建立在类型层级的基础上的。或者说,接口继承在遵循里氏代换原则的前提下,通过接口重用达成规范重用,保证了多态抽象,进而维护了开闭原则,让客户一劳永逸地享受接口服务而无后顾之忧。

为什么要提倡接口继承,慎用实现继承?

实现继承最大的硬伤是在类与类之间建立了强耦合关系,使代码趋于僵硬、脆弱和复杂。这种关系是永固的,一经建立无法解除;这种关系是纵深的,不限于相邻的父类与子类,更贯穿整个类族;这种关系还是双向的,从上到下、从下到上。如果想要覆盖某一方法成员,还得了解祖先类成员之间的内在逻辑关系;如果想增加一个方法成员,还得祈祷它不与祖先类将来的版本发生冲突。

你对方法修饰符的选择标准是什么?

推荐的方法修饰符:

方法修饰符/方法职责 公开接口 内部挂钩 内部接口 自用
访问控制 public protected或private(C++) protected或package(Java)intertal(C#) private
多态 非多态 多态 非多态 非多态