2.30 has-a关系的重要性
C++面向对象高效编程(第2版)
“has-a”关系(也称为关联、聚集、包含、组合)是在OOD(面向对象设计)中频繁使用的重要关系。但是,许多设计者和程序员都没有很好地理解其相关性,从而导致复杂僵化的设计和不必要的继承。
在OOD阶段,软件的复用主要通过两种方式完成:继承和包含。它们各有优缺点。优秀的设计者了解它们的局限性、优点和代价,可以灵活自如地应用它们。继承将在第5章、第6章以及第二部分的第12章中详细讨论。
包含是一项强大的设计技术,它比继承更容易实现和管理。包含是对接口编程的真正应用。再回到Car包含Engine的例子,Car类对象对于它所包含的Engine类对象没有特权。Car类对象只通过它的接口操作(或使用)Engine类对象,绝对不会违反封装原则。进一步而言,Car中包含Engine类对象只是一个实现细节,Car的客户无需知道这些细节。Car抽象提供了一个定义良好的接口,Car的客户只需理解Car的接口和行为。Car的实现者也可以在运行期自由替换Engine类型(支持Engine的接口)的不同对象。例如,如果
图2-30
Car类对象持有一个Engine类对象的指针(或引用),在运行期,可能会用某些支持Engine接口的其他对象替换现有对象。这种改变不会影响Car的客户,因为改变的是内部的实现细节(详见第二部分第11章)。
利用聚集,可以用不同的对象组合出新的行为。我们可以通过改变内部实现对象,来为现有对象添加新的性能。这使得软件更加灵活,能适应不同的修改需要。
但是,“has-a”关系也有它们共同的缺点。当使用带有包含关系的对象时,由于额外的间接层次太多,导致运行期的效率太低。带有聚集关系的对象,其行为更加难以理解和描述,因为它们的行为在运行期很容易改变。要实现可靠的包含关系还要对资源管理特别留心(避免资源泄漏)。
总而言之,在设计抽象时,要特别注意类与类之间的关系。如果决定使用继承和包含关系,要额外小心。在学习完继承后,我们将在第6章末尾重新讨论这个问题。
阅读:
了解Smalltalk中的消息(方法)关系。
研究Eiffel中的接口与实现分离。
学习Modula-2中的模块(module)概念,并与对象作比较。
学习不同语言中的动态绑定(dynamic binding)和静态绑定(static binding)。
了解Ada中的程序包(package)概念。
理解Java中的类和接口模型。
本文仅用于学习和交流目的,不代表异步社区观点。非商业转载请注明作译者、出处,并保留本文的原始链接。