讲师中心 微信公众号
AI工具推荐 视频效率加速

组合与继承的区别_为什么说“多用组合,少用继承”

阿涛同学_1734

阿涛同学_1734

发布时间:2026-02-19 13:43:36

|

799人浏览过

|

来源于php中文网

原创

优先使用组合而非继承:组合支持运行时替换行为、便于测试和解耦;继承适用于稳定 is-a 关系且无业务语义的模板类,如 AbstractList;滥用继承会导致僵化与维护困难。

组合与继承的区别_为什么说“多用组合,少用继承”

继承写多了,编译期就卡死,运行时改不动

继承关系在 Java、C# 等语言里是编译期绑定的:子类一写 extends Parent,父类结构就锁死了。一旦 Parent 里加个 protected 方法或改个构造逻辑,所有子类都得跟着测——哪怕它们根本不用这个功能。

组合则完全相反:class Bird { private FlyBehavior flyBehavior; },flyBehavior 可以在运行时换成 new RocketFly() 或 new NoFly(),连重新编译都不用。

  • 多层继承(比如 A → B → C → D)会让调试像扒洋葱:改一行 B 的代码,D 的行为突然异常,但堆栈里根本看不出关联
  • 想给已有类“动态加能力”?继承做不到;组合只要换掉成员对象就行
  • 单元测试时,继承让 mock 变得困难——你得 mock 整个父类链;组合只需 mock 一个接口实现

is-a 还是 has-a?别硬套,先看领域语义

很多人误以为“鸭子是鸟,所以 Duck extends Bird”天经地义。但现实里,Bird 如果定义了 fly(),那企鹅和鸵鸟就只能重写成抛异常——这不是建模,是埋雷。

真正该问的是:这个类“需要什么行为”,而不是“它属于哪个分类”。fly() 不是鸟的本质属性,而是可插拔的能力。

  • 当语义是“是一个”且稳定不变(如 IOException extends Exception),继承合理
  • 当语义是“有一个”“能做某事”(如 Order 有 PaymentStrategy、有 NotificationService),必须用组合
  • 看到抽象类名带 Abstract + Base + 多层派生(如 BaseDao → JdbcBaseDao → MySqlBaseDao),大概率是组合没用好

Java 里怎么写组合?不是 new 就完事

组合不是简单在类里 new 一个对象,关键在解耦和可替换。直接 private DatabaseHelper helper = new DatabaseHelper(); 和继承一样僵硬。

正确做法是依赖接口或抽象类型,把具体实现推迟到外部注入:

class UserService {
    private final UserRepository userRepository; // 接口,非具体类
    UserService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }
}
  • 永远优先声明为接口(UserRepository)而非实现类(JdbcUserRepository)
  • 构造器注入优于 new,便于测试和替换
  • 避免在组合对象里暴露内部细节:不要让 UserService 调用 userRepository.getJdbcTemplate()

什么时候真该用继承?别被教条吓住

组合不是银弹。有些场景继承更轻量、更直观,强行组合反而绕远路。

典型例子:ArrayList 继承 AbstractList,HashMap 继承 AbstractMap——这些基类只提供通用骨架,不带业务语义,也不对外暴露 protected 实现细节,改动风险极低。

  • 父类是 final 或纯模板方法(templateMethod() 调用抽象 doSomething()),且无状态、无副作用
  • 子类数量极少(≤3),且生命周期与父类强绑定(如 Swing 组件 JButton extends JButton)
  • 性能敏感路径(组合多一层虚方法调用,但现代 JVM 基本优化掉了,除非在 nanotime 级别压测)

真正难的不是选组合还是继承,而是识别出那个“不该被继承”的父类——它往往已经悄悄长出了三个以上 protected 方法,还被五个子类各自重写了不同逻辑。

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热门AI工具

更多
Laper
Laper Hot

Laper是专为编剧、导演和制片人推出的 AI 原生剧本创作工具。

WorkBuddy

一款AI办公效率工具,主要用于腾讯云推出的AI原生桌面智能体工作台,适合需要提升相关任务效率的用户。

火山引擎

火山引擎是一款面向企业的云计算与AI服务平台。

Loomy
Loomy Hot

一款AI工具,主要用于科大讯飞发布的桌面级 AI 助理,比 OpenClaw 更易用、更安全!,适合需要提升相关任务效率的用户。

DeepSeek

DeepSeek是一款面向对话、写作、编程和推理场景的AI大模型工具。

讯飞智作

讯飞智作是一款AI视频创作工具,AI文本配音工具,数字人课程、营销视频制作。

豆包大模型

豆包大模型是一款由字节跳动推出的企业级大语言模型服务平台。

SkildArt
SkildArt Hot

SkildArt是一款AI文本写作工具,一站式 AI 视觉创作平台。

墨刀AI
墨刀AI Hot

一款AI图像与设计工具,主要用于产品经理的专属智能体,适合需要提升相关任务效率的用户。

相关专题

更多
硬盘接口类型介绍
硬盘接口类型介绍

硬盘接口类型有IDE、SATA、SCSI、Fibre Channel、USB、eSATA、mSATA、PCIe等等。详细介绍:1、IDE接口是一种并行接口,主要用于连接硬盘和光驱等设备,它主要有两种类型:ATA和ATAPI,IDE接口已经逐渐被SATA接口;2、SATA接口是一种串行接口,相较于IDE接口,它具有更高的传输速度、更低的功耗和更小的体积;3、SCSI接口等等。

3088

2023.10.19

PHP接口编写教程
PHP接口编写教程

本专题整合了PHP接口编写教程,阅读专题下面的文章了解更多详细内容。

4569

2025.10.17

php8.4实现接口限流的教程
php8.4实现接口限流的教程

PHP8.4本身不内置限流功能,需借助Redis(令牌桶)或Swoole(漏桶)实现;文件锁因I/O瓶颈、无跨机共享、秒级精度等缺陷不适用高并发场景。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

3729

2025.12.29

java接口相关教程
java接口相关教程

本专题整合了java接口相关内容,阅读专题下面的文章了解更多详细内容。

426

2026.01.19

堆和栈的区别
堆和栈的区别

堆和栈的区别:1、内存分配方式不同;2、大小不同;3、数据访问方式不同;4、数据的生命周期。本专题为大家提供堆和栈的区别的相关的文章、下载、课程内容,供大家免费下载体验。

5207

2023.07.18

堆和栈区别
堆和栈区别

堆(Heap)和栈(Stack)是计算机中两种常见的内存分配机制。它们在内存管理的方式、分配方式以及使用场景上有很大的区别。本文将详细介绍堆和栈的特点、区别以及各自的使用场景。php中文网给大家带来了相关的教程以及文章欢迎大家前来学习阅读。

2308

2023.08.10

堆和栈的区别
堆和栈的区别

堆和栈的区别:1、内存分配方式不同;2、大小不同;3、数据访问方式不同;4、数据的生命周期。本专题为大家提供堆和栈的区别的相关的文章、下载、课程内容,供大家免费下载体验。

5207

2023.07.18

堆和栈区别
堆和栈区别

堆(Heap)和栈(Stack)是计算机中两种常见的内存分配机制。它们在内存管理的方式、分配方式以及使用场景上有很大的区别。本文将详细介绍堆和栈的特点、区别以及各自的使用场景。php中文网给大家带来了相关的教程以及文章欢迎大家前来学习阅读。

2308

2023.08.10

LLVM自定义Pass怎么写
LLVM自定义Pass怎么写

本专题聚焦LLVM自定义Pass开发,整理Pass类结构、run()方法、PreservedAnalyses、CMake构建、插件注册、-load-pass-plugin加载和测试用例编写流程。

100

2026.09.30

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
热门推荐
/
最新课程
关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn