为什么讲复合优先于继承

从《Effective Java》中总结
封装,继承,多态是面向对象类语言的三个核心特性,但是我们经常看到教材或者各种书上会讲复合优先于继承,为什么呢?

首先不是说继承不如复合,复合更像是修饰者模式,基类和包装器类的关系,而继承就是父类和子类的关系
下面说的都是基于子类扩展父类(实现继承),而不是接口继承(一个类实现一个接口,或者一个接口扩展另一个一个接口)

一 在实际开发中继承的缺点:

  1. 与方法调用不同,继承打破了封装性:子类依赖于其超类中的特定功能的实现细节,
  2. 如果需要覆盖超类的方法,就必须要知道超类所有的方法的内部逻辑,否则会照成意想不到的事故

举个例子:
父类有a,b,c三个方法,而子类覆盖了b,c两个方法,b里面有super.a,当外部调用子类的的b方法,如果a有c方法的调用,而且你不知道a方法调用了c方法,而且你还修改了c方法的逻辑,那可能会造成意想不到的结果

  1. 超类的实现会随着发行版本的不同而内部逻辑可能会有变化
  2. 如果要继承超类,超类必须要有详细的说明文档,否则增加学习成本
  3. 暴露实现细节,可能导致客户端直接访问这些内部细节
  4. 限制在原始的实现上,永远限定了类的性能
  5. 导致语义上的细节
  6. 可能客户直接修改超类,从而破坏子类的约束条件

以上缺点不适合的范围:首先项目中超类,父类,子类完全由你一个人完成,并且不会有其他人插手,别人也不会扩展你的超类,基类,也不会使用你完成的子类。

复合相比较于继承的优点:

  1. 通过在新类增加一个私有域,引用原本的超类(后面同意叫需要叫现有类),使现有类变成新类的一个组件,而新类的方法都可以调用现有类里面的对应的方法,这个也叫转发
  2. 没有打破封装,就算现有类添加新的方法,或者修改原来方法的逻辑(方法入参和返回结果不能有改变),也不会影响到新类,对于封装的优点自行查询

复合和转发的结合也被宽松的称为"委托"

复合的缺点:

不适合回调框架,因为回调框架是把对象自身的引用传递给其他的对象,用于后续的调用,但是包装起来的对象并不知道它外面的对象,所以它传递一个执行自身的引用,回调时避开了外面的包装对象,这也被称为SELF问题

四 如何选复合还是选继承

在《Effective Java》中的选择方法

  1. 是否正在扩展超类?
  2. 它的API有没有缺陷?
  3. 是否愿意把那些缺陷传播到子类的API中?
  4. 是否存在父子类型关系?

即使存在父子关系,一样可以用复合,而且超类设计的不好,会很影响子类

在我遇到的实际项目中:

  • 用到继承的大部分超类都是基类,或者子类必须要覆盖父类的指定方法
  • 而对于扩展的功能基本上都用的复合

四 用到继承时,一定要有文档

而现实是我们用到继承的地方,也就在超类上写明超类的含义,可能方法都不写注释,所以现在我一直提倡注释不要怕写多,只要能描述清楚,不写注释还要我们研究代码,浪费时间,

  1. 超类的构造器不要调用子类可覆盖的方法(直接间接都不行)
  2. clone和readObject不可调用可覆盖的方法(直接间接都不行)
  3. 对于不想让子类覆盖的方法,就要禁止此方法子类化
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值