为什么说组合优于继承?

news2024/11/26 20:18:10

在编程中,继承和组合是用于在面向对象语言中设计和构建类和对象的两种基本技术。

继承,它允许一个类(称为派生类或子类)从另一个类(称为基类或超类)继承属性和行为。换句话说,子类“是”超类的一种类型。它建立了一种“是”关系。例如,如果我们有一个类“Animal”和一个类“Dog”,则“Dog”类继承自“Animal”,因为狗是一种动物。

组合,涉及使用其他对象作为组件来构建对象。类不是继承属性和行为,而是使用其他类的实例来实现其功能。它建立了“有”关系。例如,“Car”类可以具有“Engine”类和“Wheel”类的组合。

优势劣势相关设计模式
继承(1)允许子类从超类继承属性和方法来促进代码重用。
(2)有助于在单个继承树下组织和抽象相关类。
(3)通过遵循清晰的层次结构来简化对类结构的理解。
(4)协助定义子类的通用接口和协定。
(5)通过在类结构中反映逻辑关系来增强代码的可读性。


(1)可能导致类之间的高度耦合,使代码更难维护和修改,超类中的修改可能会影响所有子类。
(2)随着时间的流逝,继承层次结构通常会变得复杂且难以管理,可能会增加代码复杂性,使类理解更具挑战性。
(3)添加新类可能需要对现有层次结构进行重大更改。
(4)如果访问限制管理不当,可能会引入安全问题。
(5)当创建大量子类实例时,可能会导致内存消耗过高。

工厂模式
组合(1)促进低耦合,通过允许通过合成组合和自定义对象来提供更大的灵活性。
(2)允许在不影响主类的情况下修改组件,从而简化更新。
(3)通过“有”关系,促进组件聚合复杂对象的创建。
(4)可以通过避免继承开销来提高性能和强耦合等继承问题。
(5)促进更加模块化和易于理解的代码结构。
(1)可能会导致创建由大量组件组成的对象,这些组件的维护可能很复杂。
(2)缺乏明确的阶级层次结构。
(3)在大型应用程序中,组件生命周期管理可能很复杂,需要更大的初始设计投资来定义适当的组合关系。
装饰者模式、策略模式

在面向对象编程中,组合通常被认为优于继承,这主要是因为组合提供了一种更为灵活和可维护的方式来构建和扩展类的功能。

代码复用与扩展性。 通过继承,子类可以自动获取父类的所有属性和方法,实现代码的复用。但这也可能导致类的层次结构变得复杂,增加代码维护的难度。同时,当父类发生改变时,子类可能也需要相应的调整。通过组合,一个类可以将其他类的对象作为自己的成员变量来使用,从而复用这些对象的功能。这种方式更为灵活,因为被组合的类(成员变量)可以独立地改变和扩展,而不需要修改包含它们的类。

降低类之间的耦合度。 在继承关系中,子类与父类之间存在紧密的耦合关系,子类对父类的任何修改都可能产生影响。通过组合,类之间的关系更为松散,一个类的改变通常不会影响到其他类,除非它们共享相同的成员变量。

以汽车和发动机为例。如果我们使用继承来表示汽车和发动机的关系,可能会定义一个“汽车”类,然后定义一个“电动汽车”类继承自“汽车”类,并添加与电池和电机相关的属性和方法。但这种设计可能导致层次结构复杂,且不易于扩展其他类型的汽车(如混合动力汽车)。相反,我们可以通过组合关系,定义一个“汽车”类,它包含一个“发动机”对象作为成员变量。然后,我们可以定义不同类型的发动机类(如汽油发动机、柴油发动机、电动机等),并将它们作为参数传递给“汽车”类的构造函数。这样,我们可以轻松地创建不同类型的汽车,而无需修改“汽车”类本身。此外,我们还可以独立地测试“汽车”类和各种“发动机”类。

为什么Go、Rust等新兴语言舍弃了继承特性

Go和Rust等新兴语言选择不直接支持传统面向对象编程(OOP)中的继承特性,而是采用了其他机制来实现代码复用和扩展性,这主要是基于以下几个原因:

简洁性:Go和Rust的设计目标之一就是保持语言的简洁性。传统面向对象编程中的继承机制往往会引入复杂的层级结构和方法重写规则,增加了代码的复杂性。为了保持语言的简洁和易读性,Go和Rust选择了更简单的代码复用机制,如组合(composition)和接口(interface)。

灵活性:继承机制在编译时确定了类的结构,这限制了代码的灵活性和可适应性。而组合允许对象动态地获取、替换、增加或删除其行为,使代码更加灵活和可扩展。Go和Rust通过接口和trait提供了类似的功能,允许开发者以更灵活的方式组织代码。

正交性:继承机制通常与类、对象、封装等其他OOP特性紧密相关,这可能导致设计上的耦合和限制。Go和Rust更倾向于保持语言特性的正交性,即每个特性都可以独立使用,而不需要依赖于其他特性。因此,它们选择了更加独立的机制来实现代码复用和扩展性。

避免过度使用继承:在实践中,过度使用继承可能导致类层级过深、功能耦合紧密、代码难以维护等问题。Go和Rust的设计者意识到这些问题,并希望通过提供更简单的代码复用机制来避免过度使用继承。它们鼓励开发者使用组合和接口/trait来实现代码复用,这有助于保持代码的清晰和可维护性。

编译时检查和内存安全:Rust特别关注编译时检查和内存安全。继承机制可能使得编译器难以在编译时检查类型和行为的一致性,从而增加了内存不安全的风险。Rust通过trait系统提供了类似继承的功能,但更加严格地要求类型的一致性,有助于编译器在编译时发现问题,保证程序的内存安全。

小总结

继承和组合之间的选择取决于软件设计的要求和目标。一般来说,建议尽可能使用组合,以避免强耦合。当需要建立明确的“是”关系和类层次结构时,继承很有用,但应谨慎使用,以避免长期设计问题。在许多情况下,继承和组合的平衡组合可能是最佳解决方案。

参考:

https://medium.com/@josueparra2892/comparing-inheritance-and-composition-b27c8f93299a

https://zhuanlan.zhihu.com/p/60282972

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1803199.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

独立游戏之路 -- 获取OAID提升广告收益

Unity 之 获取手机:OAID、IMEI、ClientId、GUID 前言一、Oaid 介绍1.1 Oaid 说明1.2 移动安全联盟(MSA) 二、站在巨人的肩膀上2.1 本文实现参考2.2 本文实现效果2.3 本文相关插件 三、Unity 中获取Oaid3.1 查看实现源码3.2 工程配置3.3 代码实现3.4 场景搭建 四、总…

编译和运行qemu-uboot-arm64单板的Armbian系统

这篇文章ARM虚拟机安装OMV-CSDN博客遗留一个启动qemu-uboot-arm64单板Armbian镜像的问题,使用官方下载的镜像,会报错: fatal: no kernel available .... Failed to load /vmlinuz ...... qemu-system-aarch64 -smp 8 -m 8G -machine virt …

搭建多平台比价系统需要了解的电商API接口?

搭建一个多平台比价系统涉及多个步骤,以下是一个大致的指南: 1. 确定需求和目标 平台选择:确定你想要比较价格的平台,例如电商网站、在线旅行社等。数据类型:明确你需要收集哪些数据,如产品价格、产品名称…

苹果手机微信如何直接打印文件

在快节奏的工作和生活中,打印文件的需求无处不在。但你是否曾经遇到过这样的困扰:打印店价格高昂,让你望而却步?今天,我要给大家介绍一款神奇的微信小程序——琢贝云打印,让你的苹果手机微信直接变身移动打…

二叉树的实现(初阶数据结构)

1.二叉树的概念及结构 1.1 概念 一棵二叉树是结点的一个有限集合,该集合: 1.或者为空 2.由一个根结点加上两棵别称为左子树和右子树的二叉树组成 从上图可以看出: 1.二叉树不存在度大于2的结点 2.二叉树的子树有左右之分,次序不能…

后端启动项目端口冲突问题解决

后端启动项目端口冲突 原因: Vindows Hyper-V虚拟化平台占用了端口。 解决方案一: 查看被占用的端口范围,然后选择一个没被占用的端口启动项目。netsh interface ipv4 show excludedportrange protocoltcp 解决方案二: 禁用H…

调试环境搭建(Redis 6.X 版本)

今儿,我们来搭建一个 Redis 调试环境,目标是: 启动 Redis Server ,成功断点调试 Server 的启动过程。使用 redis-cli 启动一个 Client 连接上 Server,并使用 get key 指令,发起一次 key 的读取。 视频可见…

鸿蒙状态管理-@Builder自定义构建函数

Builder 将重复使用的UI元素抽象成一个方法 在build方法里调用 使其成为 自定义构建函数 Entry Component struct BuilderCase {build() {Column(){Row(){Text("西游记").fontSize(20)}.justifyContent(FlexAlign.Center).backgroundColor("#f3f4f5").hei…

技术革命的十年:计算机、互联网、大数据、云计算与AI

近10年来,计算机、互联网、大数据、云计算和人工智能等技术领域发展迅速,带来了巨大的变革和创新。以下是各个领域的发展历史、现状、问题瓶颈、未来趋势以及可能的奇点。 计算机技术: 发展历史: 过去:过去十年间&am…

SpringBoot+Vue学生作业管理系统【附:资料➕文档】

前言:我是源码分享交流Coding,专注JavaVue领域,专业提供程序设计开发、源码分享、 技术指导讲解、各类项目免费分享,定制和毕业设计服务! 免费获取方式--->>文章末尾处! 项目介绍047: 【…

使用OpenPCDet训练与测试多传感器融合模型BEVFusion,OPenPCdet代码架构介绍

引言 在自动驾驶领域,多传感器融合技术是一种常见的方法,用于提高感知系统的准确性和鲁棒性。其中,BevFusion是一种流行的融合方法,可以将来自不同传感器的数据进行融合,生成具有丰富信息的鸟瞰图(BEV&…

应用广义线性模型二|二响应广义线性模型

系列文章目录 文章目录 系列文章目录一、二响应模型的不同表达方式和响应函数二、二响应模型的性质(一)二响应变量的条件数学期望与方差(二)二响应模型参数的极大似然估计(三)二响应模型的优势 三、二响应模…

vue2 中如何使用 render 函数编写组件

vue2 中如何使用 render 函数编写组件 render 基础语法createElement返回值:VNode参数处理样式和类组件 propsHTML 特性和 DOM 属性处理事件插槽指令v-model 指令其他属性 使用 render 封装一个输入框其他问题参考 vue 提供了声明式编写 UI 的方式,即 vu…

Java+Spring boot+MYSQL 技术开发的UWB室内外高精度一体化融合定位系统源码 UWB技术定位系统应用场景

JavaSpring bootMYSQL 技术开发的UWB室内外高精度一体化融合定位系统源码 UWB技术定位系统应用场景 系统聚焦基于UWB(超宽带)技术的底层定位网络和定位算法,通过对定位分站、定位标签、定位引擎的硏发,实现高精度定位网络,获取高精度定位结果…

翻译《The Old New Thing》- Why isn’t there a SendThreadMessage function?

Why isnt there a SendThreadMessage function? - The Old New Thing (microsoft.com)https://devblogs.microsoft.com/oldnewthing/20081223-00/?p19743 Raymond Chen 2008年12月23日 为什么没有 SendThreadMessage 函数? 简要 文章讨论了 Windows 中不存在 Sen…

用QT6、QML、FFMPEG写一个有快进功能的影音播放程序

程序如图: 开发环境在ubuntu下,如果改windows下,也就改一下cmakelists.txt。windows下如何配置ffmpeg以前的文章有写,不再重复。 源程序如下: GitHub - wangz1155/ffmpegAudioThread: 用qt6,qml&#xff…

深度图的方法实现加雾,Synscapes数据集以及D455相机拍摄为例

前言 在次之前,我们已经做了图像加雾的一些研究,这里我们将从深度图的方法实现加雾展开细讲 图像加雾算法的研究与应用_图像加雾 算法-CSDN博客 接下来将要介绍如何使用深度图像生成雾效图像的方法。利用Synscapes数据集,通过读取EXR格式的…

Linux☞进程控制

在终端执行命令时,Linux会建立进程,程序执行完,进程会被终止;Linux是一个多任务的OS,允许多个进程并发运行; Linxu中启动进程的两种途径: ①手动启动(前台进程(命令gedit)...后台进程(命令‘&’)) ②…

重构大学数学基础_week04_从点积理解傅里叶变换

这周我们来看一下傅里叶变换。傅里叶变换是一种在数学和许多科学领域中广泛应用的分析方法,它允许我们将信号或函数从其原始域(通常是时间域或空间域)转换到频域表示。在频域中,信号被表示为其组成频率的幅度和相位,这…

STM32F103C8T6基于HAL库完成uC/OS-III多任务程序

一、在STM32CubeMX中建立工程 配置RCC 配置SYS 配置PC13为GPIO_Output 配置USART1 生成代码 二、获取uC/OS-III源码 官网下载地址:Micrium Software and Documentation - Silicon Labs 网盘下载:百度网盘 请输入提取码 提取码:lzjl 三、复…