设计模式之美
王争
前 Google 工程师,《数据结构与算法之美》专栏作者
123425 人已学习
新⼈⾸单¥98
登录后,你可以任选6讲全文学习
课程目录
已完结/共 113 讲
设计模式与范式:行为型 (18讲)
设计模式之美
15
15
1.0x
00:00/00:00
登录|注册

10 | 理论七:为何说要多用组合少用继承?如何决定该用组合还是继承?

使用设计模式、特殊场景固定使用继承或组合
稳定、浅层次、不复杂可使用继承
根据具体情况选择使用继承或组合
解决继承关系复杂性问题
通过组合、接口、委托替代继承
例子:鸵鸟继承具有fly()方法的父类
继承层次过深、过复杂影响可维护性
如何判断该用组合还是继承
组合优势
继承的问题
为何说要多用组合少用继承?

该思维导图由 AI 生成,仅供参考

在面向对象编程中,有一条非常经典的设计原则,那就是:组合优于继承,多用组合少用继承。为什么不推荐使用继承?组合相比继承有哪些优势?如何判断该用组合还是继承?今天,我们就围绕着这三个问题,来详细讲解一下这条设计原则。
话不多说,让我们正式开始今天的学习吧!

为什么不推荐使用继承?

继承是面向对象的四大特性之一,用来表示类之间的 is-a 关系,可以解决代码复用的问题。虽然继承有诸多作用,但继承层次过深、过复杂,也会影响到代码的可维护性。所以,对于是否应该在项目中使用继承,网上有很多争议。很多人觉得继承是一种反模式,应该尽量少用,甚至不用。为什么会有这样的争议?我们通过一个例子来解释一下。
假设我们要设计一个关于鸟的类。我们将“鸟类”这样一个抽象的事物概念,定义为一个抽象类 AbstractBird。所有更细分的鸟,比如麻雀、鸽子、乌鸦等,都继承这个抽象类。
我们知道,大部分鸟都会飞,那我们可不可以在 AbstractBird 抽象类中,定义一个 fly() 方法呢?答案是否定的。尽管大部分鸟都会飞,但也有特例,比如鸵鸟就不会飞。鸵鸟继承具有 fly() 方法的父类,那鸵鸟就具有“飞”这样的行为,这显然不符合我们对现实世界中事物的认识。当然,你可能会说,我在鸵鸟这个子类中重写(override)fly() 方法,让它抛出 UnSupportedMethodException 异常不就可以了吗?具体的代码实现如下所示:
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结

面向对象编程中,组合优于继承的设计原则是为了解决继承层次过深、过复杂导致的可维护性和可读性问题。文章通过举例说明了继承在表示is-a关系时可能存在的问题,以及通过组合、接口、委托三个技术手段来替代继承的优势。通过组合和接口的has-a关系来替代is-a关系,利用接口实现多态特性,通过组合和委托实现代码复用,完全可以替换掉继承。因此,在项目中应该尽量避免过深、过复杂的继承关系,特别是一些复杂的继承关系。文章通过具体的代码实现和实际案例,详细解释了为何要多用组合少用继承,以及如何决定该用组合还是继承。 文章还提到了如何判断该用组合还是继承,指出在实际项目开发中,应根据具体情况选择使用继承还是组合。稳定的继承结构、浅层次、不复杂的关系可以使用继承,而不稳定、深层次、复杂的关系则应尽量使用组合。此外,特殊的设计模式和应用场景也会固定使用继承或组合。最后,总结了为什么不推荐使用继承、组合相比继承的优势以及如何处理代码重复的问题。 总的来说,本文深入浅出地解释了组合优于继承的设计原则,通过具体案例和技术手段的对比,使读者能够清晰地理解何时该使用组合,何时该使用继承,以及它们各自的优势和劣势。

仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《设计模式之美》
新⼈⾸单¥98
立即购买
登录 后留言

全部留言(228)

  • 最新
  • 精选
  • 探索无止境
    我个人感觉VO和BO都会采用组合entity的方式,老师是否可以在下一节课课聊聊上节课留下的思考题,您的处理方式?

    作者回复: 抽空集中答疑一下吧

    2019-11-25
    30
    168
  • Cy23
    打卡✔ 继承的层次过深带来的缺点看明白了,组合和委托不太理解,回头又好好看了看代码,视乎理解了,希望不要忘记了。也不知道是否理解对了? 记下自己的体会: 组合——类中包含其他实现类,感觉就是把大的功能分成多个类来实现,然后再根据需要组合进来使用。 委托——类中实现接口方法的时,把具体的实现方法调用其他类中的方法处理,也就是委托给别人(被委托者)帮他写好具体的实现。

    作者回复: 理解的没问题!

    2019-11-25
    2
    31
  • cc
    希望作者能在课程末尾梳理下上一节课程的课后习题,或者集中点评下大家的留言。感谢

    作者回复: 可以的...

    2019-11-25
    2
    23
  • Hua100
    请教一下,java8之后接口可以有default,那是不是就可以不需要使用组合+接口+委托了呢?是不是只需要接口+default方法就可以了?类似这样: public interface Fly { default void fly(){ // 具体操作 } } // Tweetable, Eggable同理 public class Ostrich implements Tweetable, Eggable { public void egg(){ Eggable.super.egg(); } // tweet同理 } 不知道我的理解对不对,我感觉这样就没必要用三个技术结合的方式。求大佬解答。

    作者回复: 是的,你理解的没错。但是,并不是所有的语言中,接口都支持默认实现这个特性。

    2020-07-26
    2
    14
  • ANYI
    这个课堂讨论,争哥啥时候可以给大家讲解下,这个貌似都是大家比较关注的点,😄

    作者回复: 恩恩,好的,抽空答疑一下

    2019-11-26
    3
    3
  • Miaozhe
    老师,针对你举鸟的例子:使用组合+接口+委托 代替继承的例子。如果在Spring中,怎么通过接口(自动注入)调用Ostrich鸵鸟的实现方法(下蛋和叫)?Tweetable和EggLayable接口各定义一个接口变量由鸵鸟初始化?这样感觉雍余了。 请解答一下?

    作者回复: 你的意思是通过接口调用两个方法对吧,如果实在有这个需求,可以重新定义一个接口,extends另外两个接口。不过,你说的spring注入的情况,完全可以不依赖接口呀,直接依赖实现类也可以的。

    2019-11-27
    2
  • 小先生
    请问老师,好多类想要拥有相同的一个属性,考虑到代码复用性,是否只能继承啦

    作者回复: 也可以用组合的,组合也能解决复用的问题

    2019-11-25
    2
  • 欠债太多
    我们现在采用entity实现,VO和BO都去继承它,减少代码重复。看了专栏后,我认为可以通过讲VO和BO组合成Entity实现,不知道这样做,是不是合适

    作者回复: 好像不合适,怎么通过vo和bo组合成entity呢?

    2019-11-25
    2
  • Jessica
    老师能不能加餐集中解答一周的问题,有些老师提问的刚好我们在项目中也思考过,就是没找到很好的解决方案

    作者回复: 可以的 等后面有时间了吧 年底比较忙

    2019-12-29
    2
    1
  • 西电
    请教一下,如果是按照功能分成多个基类,然后再按需继承。 比如将大的基类分成,Fly类,Tweet类,Egg类三个类。 鸵鸟就只继承Tweet类,Egg类 这样也可以实现接口,组合,委托的效果啊,请问一下这样有什么坏处呢?

    作者回复: 有些语言不支持多重继承,而且从语义上也不对,什么是Fly类啊,飞是行为,更适合用接口来表达,不适合作为类吧

    2020-07-15
收起评论
显示
设置
留言
99+
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部