继承还是关联?是个问题

     当我们的讲面向对象或系统设计的老师又在课堂上向我们传授在进行复用时“尽量使用合成/聚合,而不是使用继承”的“定理”的时候,可能我们仿佛真的找到了问题的解决方法,但是这背后意味着什么呢?我们为什么需要这样做呢?是否这是问题的最佳解决方案了呢?这一切的问题起源于一个名为“合成聚合复用原则”的OOD原则...
     下面还是通过CSDN上Health King的专栏的一篇文章来进行讲述吧!涂了颜色[ ]的是我自己做得注释。
     合成(Composition)和聚合(Aggregation)都是关联(Association)的特殊种类。聚合表示整体和部分的关系,表示“拥有”;合成则是一种更强的“拥有”,部分和整体的生命周期一样。合成的新的对象完全支配其组成部分,包括它们的创建和湮灭等。一个合成关系的成分对象是不能与另一个合成关系共享的。
     换句话说,合成是值的聚合(Aggregation by Value),而一般说的聚合是引用的聚合(Aggregation by Reference)。
     [不过好像现在对合成和聚合不是太追究了,特别是在UML2.0里面,所以这个问题我们知道就可以了]
     简短的说,合成-聚合复用原则(CARP)是指:尽量使用合成/聚合,而不是使用继承。 [定理出现了]
     在OOD中,有两种基本的办法可以实现复用,一种是通过合成/聚合,另外一种就是通过继承[当然还有好多扩展型的设计模式可以拿来用,例如最长见的Decorator模式,这都是后话了]。通过合成/聚合的好处是:
     ----- 新对象存取成分对象的唯一方法是通过成分对象的接口。 [恩,符合依赖倒转原则,赞!]
     ----- 这种复用是黑箱复用,因为成分对象的内部细节是新对象所看不见的。 [降低了复用的复杂性]
     ----- 这种复用支持包装。 [不知道是不是在说Decorator模式呢!]
     ----- 这种复用所需的依赖较少。 [想要关联谁,我就关联谁,我的地盘我做主,哈哈]
     ----- 每一个新的类可以将焦点集中在一个任务上。 [可以将一些子任务分派的成分对象里面,而本身只关心自己的任务,这也是设计一个对象所要考虑的,每个对象都有自己的责任,他只需要把自己的责任做好就足够了,不要去干涉其他的对象的责任,看来,我们的系统我们不需要多管闲事的对象]
     ----- 这种复用可以在运行时间内动态进行,新对象可以动态的引用与成分对象类型相同的对象。 [增加了复用的灵活性,对复用可以进行灵活的配置]
     ----- 作为复用手段可以应用到几乎任何环境中去。 [这句话有一些不明白了?]
     它的缺点就是系统中会有较多的对象需要管理。 [还是不能避免好的设计会带来复杂性的问题]
     通过继承来进行复用的优点是:
     -----新的实现较为容易,因为超类的大部分功能可以通过继承的关系自动进入子类。
     ----- 修改和扩展继承而来的实现较为容易。
     通过继承来实现复用的缺点是:
     ----- 继承复用破坏包装,因为继承将超类的实现细节暴露给子类。由于超类的内部细节常常是对于子类透明的,所以这种复用是透明的复用, 又称“白箱”复用。 [但是有时这种透明,会代来不好的结果,可能有的时候我们只想去用一个功能,而并不想弄清楚功能是怎么实现的]
     ----- 如果超类发生改变,那么子类的实现也不得不发生改变。 [又是改变,但谁能避免改变呢,我们能唯一确定不变的就是改变]
     ----- 从超类继承而来的实现是静态的,不可能在运行时间内发生改变,没有足够的灵活性。
     ----- 继承只能在有限的环境中使用。 [如果有一个final类,再有本事,我们也不能继承它,所以只能去用关联了]
     要正确的选择合成/复用和继承,必须透彻的理解里氏代换原则和Coad法则。里氏代换原则前面学习过,Coad法则由Peter Coad提出,总结了一些什么时候使用继承作为复用工具的条件。 [这个原则我认为是最重要的,它给我们面对“继承还是关联”问题时一个更加细致和合理指导,而不是象生搬硬套的定理一样强暴我们的思维]只有当以下的Coad条件全部被满足时,才应当使用继承关系:
     ----- 子类是超类的一个特殊种类,而不是超类的一个角色,也就是区分“Has-A”和“Is-A”。只有“Is-A”关系才符合继承关系,“Has-A”关系应当用聚合来描述。 [这个问题看似简单,但是实际上是很难分辨的,只有慢慢地积累才会变成“火眼金睛”]
     ----- 永远不会出现需要将子类换成另外一个类的子类的情况。如果不能肯定将来是否会变成另外一个子类的话,就不要使用继承。 [就像前面所说的,利用继承进行复用是静态的,所以运行时或者将来的改变是继承的硬伤]
     ----- 子类具有扩展超类的责任,而不是具有置换调(override)或注销掉(Nullify)超类的责任。如果一个子类需要大量的置换掉超类的行为,那么这个类就不应该是这个超类的子类。
     ----- 只有在分类学角度上有意义时,才可以使用继承。不要从工具类继承。 [不要进行没有意义的继承,这一点来说我们还是很容易分辨的,只要你对设计还有那么一点对“优美”的追求,就不会犯这个错误的]
     错误的使用继承而不是合成/聚合的一个常见原因是错误的把“Has-A”当成了“Is-A”。“Is-A”代表一个类是另外一个类的一种;“Has-A”代表一个类是另外一个类的一个角色,而不是另外一个类的特殊种类。 [下面的用例很不错的,我们是不是一直在犯这个错误呢,反正我是一直在犯这个错误,快来看看吧]
     我们看一个例子。如果我们把“人”当成一个类,然后把“雇员”,“经理”,“学生”当成是“人”的子类。这个的错误在于把“角色”的等级结构和“人”的等级结构混淆了。“经理”,“雇员”,“学生”是一个人的角色,一个人可以同时拥有上述角色。如果按继承来设计,那么如果一个人是雇员的话,就不可能是经理,也不可能是学生,这显然不合理。正确的设计是有个抽象类“角色”,“人”可以拥有多个“角色”(聚合),“雇员”,“经理”,“学生”是“角色”的子类。
     另外一个就是只有两个类满足里氏代换原则的时候,才可能是“Is-A”关系。也就是说,如果两个类是“Has-A”关系,但是设计成了继承,那么肯定违反里氏代换原则。 [里氏代换原则马上就会讲了,这几天在忙着面试,时间总是有点不够用...]

     原文链接:http://blog.csdn.net/kxy/archive/2005/07/06/415574.aspx

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值