设计模式 可复用面向对象软件的基础_面向对象基础设计原则:5.组合/聚合复用原则...

本文探讨了面向对象设计中的组合/聚合复用原则,建议优先使用组合/聚合而非继承来实现复用。组合/聚合提供黑箱复用,减少依赖,允许动态行为,但可能导致更多对象管理。另一方面,继承虽然易于实现和扩展,却破坏封装,增加系统复杂度。正确选择复用方式需遵循里氏替换原则和Coad法则。
摘要由CSDN通过智能技术生成

组合/聚合复用原则(Composite/Aggregation Reuse Principle,CARP)是指要尽量使用组合/聚合而非继承来达到复用目的。另一种解释是在一个新的对象中使用一些已有的对象,使之成为新对象的一部分;新的对象通过向这些对象委托功能达到复用这些对象的目的。

在面向对象的设计中,有两种方法可以实现对已有对象重用的目的,即通过组合/聚合,或者通过继承。那么,这两种不同的复用方式在可维护性方面有什么区别呢?

1)组合/聚合复用

我们知道组合/聚合都是关联关系的特殊种类,二者都是体现整体与部分的关系,也就是两个类之间的是“has-a”关系,它表示某一个角色具有某一项责任。由于组合/聚合都可以将已有的对象加入到新对象中,使之成为新对象的一部分,因此新对象可以调用已有对象的功能,从而实现对象复用。

例如,一台计算机,是由CPU、内存、输入设备、输出设备和外存等组装而成。计算机对象为整体,CPU、内存、输入设备、输出设备和外存等为部分,它们是聚合关系。如果一台计算机没有打印功能,可以加入一个打印机,使打印机成为计算机的一部分,从而重用打印机的打印功能。换一种角度来看,如果需要计算机有打印的责任,那么就可以将该责任委托给作为部分的打印机。

使用组合/聚合实现复用有如下好处:

  • 新对象存取成分对象的唯一方法是通过成分对象的接口。
  • 这种复用是黑箱复用,因为成分对象的内部细节是新对象所看不见的。
  • 这种复用所需的依赖较少。
  • 每一个新的类可以将焦点集中在一个任务上。
  • 这种复用可以在运行时间内动态进行,作为整体的新对象可以动态地引用与部分对象类型相同的对象。也就是说,组合/聚合是动态行为,即运行时行为。可以通过使用组合/聚合的方式在设计上获得更高的灵活性。

当然,这种复用也有缺点。其中最主要的缺点就是系统中会有较多的对象需要管理。

一般来说,如果一个角色得到了更多的责任,就可以使用组合/聚合关系将新的责任委派到合适的对象上。

2)继承复用

继承是面向对象语言特有的复用工具。由于使用继承关系时,新的实现较为容易,因父类的大部分功能可以通过继承的关系自动进入子类;同时,修改和扩展继承而来的实现较为容易。于是,在面向对象设计理论的早期,程序设计师十分热衷于继承,好像继承就是最好的复用手段,于是继承也成为了最容易被滥用的复用工具。然而,继承有多个缺点:

  • 继承复用破坏封装,因为继承将父类的实现细节暴露给子类。由于父类的内部细节常常是对于子类透明的,所以这种复用是透明的复用,又称“白箱”复用。
  • 如果父类发生改变,那么子类的实现也不得不发生改变。
  • 从父类继承而来的实现是静态的,也就是编译时行为,不可能在运行时间内发生改变,没有足够的灵活性。

正是因为继承有上述缺点,所以应首先使用组合/聚合,其次才考虑继承,达到复用的目的。并且在使用继承时,要严格遵循里氏替换原则。有效地使用继承会有助于对问题的理解,降低复杂度,而滥用继承会增加系统构建、维护时的难度及系统的复杂度。

要正确的选择组合/聚合和继承,必须透彻的理解里氏替换原则和Coad法则。里氏替换原则前面学习过,Coad法则由Peter Coad提出,总结了一些什么时候使用继承作为复用工具的条件。只有当以下的Coad条件全部被满足时,才应当使用继承关系:

  • 子类是父类的一个特殊种类,而不是父类的一个角色,也就是区分“has-a”和“is-a”。只有“is-a”关系才符合继承关系,“has-a”关系应当用组合/聚合来描述。
  • 永远不会出现需要将子类换成另外一个类的子类的情况。如果不能肯定将来是否会变成另外一个子类的话,就不要使用继承。
  • 子类具有扩展父类的责任,而不是具有置换(重写)或注销掉父类的责任。如果一个子类需要大量的置换掉父类的行为,那么这个类就不应该是这个父类的子类。
  • 只有在分类学角度上有意义时,才可以使用继承。不要从工具类继承。

错误的使用继承而不是组合/聚合的一个常见原因是错误的把“has-a”当成了“is-a”。“is-a”代表一个类是另外一个类的一种;“has-a”代表一个类是另外一个类的一个角色,而不是另外一个类的特殊种类。

我们看一个例子。如果我们把“人”当成一个类,然后把“雇员”、“经理”、“学生”当成是“人”的子类,如下图所示。这种设计的错误在于把“角色”的等级结构和“人”的等级结构混淆了。“经理”、“雇员”、“学生”是一个人的角色,一个人可以同时拥有上述角色。如果按继承来设计,那么如果一个人是雇员的话,就不可能是经理,也不可能是学生,这显然不合理。

69970881dbdb9ebc5d4972f1e04e385a.png
滥用继承

正确的设计是有个抽象类“角色”,“人”可以拥有多个“角色”(聚合),“雇员”、“经理”、“学生”是“角色”的子类,如下图所示。

8d278b05c0fe23abca8e5b3dc9f168af.png
使用组合/聚合

此外,只有两个类满足里氏替换原则的时候,才可能是“is-a”关系。也就是说,如果两个类是“has-a”关系,但是设计成了继承,那么肯定违反里氏替换原则。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值