为什么说组合优于继承?

2024-06-08 10:52
文章标签 组合 继承 优于

本文主要是介绍为什么说组合优于继承?,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

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

继承,它允许一个类(称为派生类或子类)从另一个类(称为基类或超类)继承属性和行为。换句话说,子类“是”超类的一种类型。它建立了一种“是”关系。例如,如果我们有一个类“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.chinasem.cn/article/1041981

相关文章

hdu4869(逆元+求组合数)

//输入n,m,n表示翻牌的次数,m表示牌的数目,求经过n次操作后共有几种状态#include<iostream>#include<algorithm>#include<cstring>#include<stack>#include<queue>#include<set>#include<map>#include<stdio.h>#include<stdlib.h>#includ

JavaSE——封装、继承和多态

1. 封装 1.1 概念      面向对象程序三大特性:封装、继承、多态 。而类和对象阶段,主要研究的就是封装特性。何为封装呢?简单来说就是套壳屏蔽细节 。     比如:对于电脑这样一个复杂的设备,提供给用户的就只是:开关机、通过键盘输入,显示器, USB 插孔等,让用户来和计算机进行交互,完成日常事务。但实际上:电脑真正工作的却是CPU 、显卡、内存等一些硬件元件。

Go组合

摘要 golang并非完全面向对象的程序语言,为了实现面向对象的继承这一神奇的功能,golang允许struct间使用匿名引入的方式实现对象属性方法的组合 组合使用注意项 使用匿名引入的方式来组合其他struct 默认优先调用外层方法 可以指定匿名struct以调用内层方法 代码 package mainimport ("fmt")type People struct{}type Pe

组合c(m,n)的计算方法

问题:求解组合数C(n,m),即从n个相同物品中取出m个的方案数,由于结果可能非常大,对结果模10007即可。       共四种方案。ps:注意使用限制。 方案1: 暴力求解,C(n,m)=n*(n-1)*...*(n-m+1)/m!,n<=15 ; int Combination(int n, int m) { const int M = 10007; int

代码随想录训练营day37|52. 携带研究材料,518.零钱兑换II,377. 组合总和 Ⅳ,70. 爬楼梯

52. 携带研究材料 这是一个完全背包问题,就是每个物品可以无限放。 在一维滚动数组的时候规定了遍历顺序是要从后往前的,就是因为不能多次放物体。 所以这里能多次放物体只需要把遍历顺序改改就好了 # include<iostream># include<vector>using namespace std;int main(){int n,m;cin>>n>>m;std::vector<i

七、Maven继承和聚合关系、及Maven的仓库及查找顺序

1.继承   2.聚合   3.Maven的仓库及查找顺序

INDEX+SMALL+IF+ROW函数组合使用解…

很多人在Excel中用函数公式做查询的时候,都必然会遇到的一个大问题,那就是一对多的查找/查询公式应该怎么写?大多数人都是从VLOOKUP、INDEX+MATCH中入门的,纵然你把全部的多条件查找方法都学会了而且运用娴熟,如VLOOKUP和&、SUMPRODUCT、LOOKUP(1,0/....,但仍然只能对这种一对多的查询望洋兴叹。   这里讲的INDEX+SMALL+IF+ROW的函数组合,

代码随想录算法训练营Day37|完全背包问题、518.零钱兑换II、377. 组合总和 Ⅳ、70. 爬楼梯(进阶版)

完全背包问题                  和01背包最大区别就是一个物品可以重复放多次,因此遍历空间时可以从前往后。 import java.util.*;public class Main{public static void main (String[] args) {Scanner sc = new Scanner(System.in);int m = sc.nextInt

【内网】ICMP出网ew+pingtunnel组合建立socks5隧道

❤️博客主页: iknow181 🔥系列专栏: 网络安全、 Python、JavaSE、JavaWeb、CCNP 🎉欢迎大家点赞👍收藏⭐评论✍ 通过环境搭建,满足以下条件: 攻击机模拟公网vps地址,WEB边界服务器(Windows Server 2008)模拟公司对外提供Web服务的机器,该机器可以通内网,同时向公网提供服务。内网同网段存在一台Windows内网服务