为什么建议多用组合少用继承

news2024/11/26 4:42:59

在面向对象编程中,组合(Composition)和继承(Inheritance)是两种实现代码复用的基本方法。以下是为什么通常建议多用组合而少用继承的一些原因:

灵活性与可扩展性

  1. 松耦合:组合通过将一个类的功能委托给另一个类来实现,而不是通过继承。这样,类之间的耦合度较低,修改一个类不会影响另一个类,使系统更灵活。
  2. 动态行为改变:组合允许在运行时改变行为,可以通过替换被组合的对象来改变功能,而继承则在编译时决定行为,缺乏灵活性。

代码复用与维护

  1. 避免复杂的继承层次:继承层次结构容易变得复杂和难以维护,特别是在多层继承时。而组合则能通过简单的对象组合来实现复杂功能,避免了复杂的继承层次。
  2. 增加代码复用:通过组合,可以重用已有的类而不需要创建新的子类,从而减少代码重复,提高代码复用性。

设计原则与模式

  1. 遵循“组合优于继承”原则:这是设计模式中的一个重要原则,强调优先使用组合而不是继承来实现代码复用和功能扩展。
  2. 符合开闭原则(OCP):使用组合更容易实现对扩展开放、对修改关闭的原则。新增功能可以通过组合新的类来实现,而不需要修改现有类。

避免继承问题

  1. 菱形继承问题:多重继承会导致菱形继承问题,即同一基类被多次继承,导致歧义和复杂性。组合则不存在这个问题,因为它是通过对象的关联来实现的。
  2. 基类变更影响:在继承关系中,如果基类发生变化,会影响所有子类。组合则相对独立,修改一个类不会直接影响组合它的类。

实际应用中的案例

  1. 策略模式:通过组合不同的策略对象,动态地改变算法或行为。
  2. 装饰者模式:通过组合多个装饰者对象,动态地为对象增加功能。

代码案例

背景

设计一个绘图应用程序,需要有不同形状的类,比如圆形(Circle)、矩形(Rectangle)等,并且这些形状可以有不同的颜色和边框样式。

使用继承实现
abstract class Shape {
    public abstract void draw();
}

class Circle extends Shape {
    public void draw() {
        System.out.println("Drawing a circle");
    }
}

class Rectangle extends Shape {
    public void draw() {
        System.out.println("Drawing a rectangle");
    }
}

class RedCircle extends Circle {
    public void draw() {
        System.out.println("Drawing a red circle");
    }
}

class BlueCircle extends Circle {
    public void draw() {
        System.out.println("Drawing a blue circle");
    }
}

class RedRectangle extends Rectangle {
    public void draw() {
        System.out.println("Drawing a red rectangle");
    }
}

class BlueRectangle extends Rectangle {
    public void draw() {
        System.out.println("Drawing a blue rectangle");
    }
}

public class Main {
    public static void main(String[] args) {
        Shape redCircle = new RedCircle();
        redCircle.draw();
        
        Shape blueRectangle = new BlueRectangle();
        blueRectangle.draw();
    }
}


这种设计的问题在于每增加一种新的形状或颜色都需要创建新的子类,类的数量会迅速增长,维护和扩展变得困难。

使用组合实现

通过组合,我们可以更灵活地设计这个系统。我们可以将颜色和形状的概念分离

// 颜色接口
interface Color {
    void applyColor();
}

class RedColor implements Color {
    public void applyColor() {
        System.out.println("Applying red color");
    }
}

class BlueColor implements Color {
    public void applyColor() {
        System.out.println("Applying blue color");
    }
}

// 形状抽象类
abstract class Shape {
    protected Color color;

    public Shape(Color color) {
        this.color = color;
    }

    abstract void draw();
}

class Circle extends Shape {
    public Circle(Color color) {
        super(color);
    }

    public void draw() {
        System.out.print("Drawing a circle with ");
        color.applyColor();
    }
}

class Rectangle extends Shape {
    public Rectangle(Color color) {
        super(color);
    }

    public void draw() {
        System.out.print("Drawing a rectangle with ");
        color.applyColor();
    }
}

public class Main {
    public static void main(String[] args) {
        Shape redCircle = new Circle(new RedColor());
        redCircle.draw();
        
        Shape blueRectangle = new Rectangle(new BlueColor());
        blueRectangle.draw();
    }
}

分析

在这个设计中,我们使用了组合(Composition)而不是继承来实现颜色和形状的结合:

  • 颜色:我们定义了Color接口和它的实现类RedColor和BlueColor。
  • 形状:我们定义了Shape抽象类,并将Color作为一个组合成员。然后,我们创建了具体的形状类Circle和Rectangle。

这样,我们可以任意组合形状和颜色,而不需要创建大量的子类。新增一种颜色或形状只需要创建一个新的实现类,而不需要修改现有的代码。这种设计更加灵活和可扩展,遵循了“组合优于继承”的设计原则。

总结

虽然继承在某些情况下仍然有用,但在大多数情况下,组合提供了更高的灵活性、可扩展性和可维护性。因此,建议在设计系统时,多使用组合,少使用继承,以构建更健壮和灵活的系统。

持续输出 欢迎关注

搜索框传播样式-白色版

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

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

相关文章

使用SpringBoot对接Kafka

Kafka是什么,以及如何使用SpringBoot对接Kafka 一、Kafka与流处理 我们先来看看比较正式的介绍:Kafka是一种流处理平台,由LinkedIn公司创建,现在是Apache下的开源项目。Kafka通过发布/订阅机制实现消息的异步传输和处理。它具有高…

Shell脚本(.sh文件)如何执行完毕之后不自动关闭?

Shell脚本异常傲娇,出错后、执行完根本不给你机会让你查看报错信息、输出信息,直接闪退。 废话不多说,调教方法如下,直接在Shell脚本末尾加上如下代码: 1、实现方式一 1.1 使用read命令达到类似bat中的pause命令效果…

构建全面框架 | 简化基因组+线粒体遗传进化联合分析

近日,凌恩生物客户河北农业大学、浙江大学及英国格林威治大学的研究团队合作,在《Insect Science》杂志上发表了题为“A comprehensive framework for the delimitation of species within the Bemisia tabaci cryptic complex, a global pest-species g…

LaTeX 的使用

文章目录 TeX 编辑器文档类型中文编译文档结构preamble 导言区(不能放正文内容)document body 正文区 正文内容目录段落列表无序列表有序列表 图片表格交叉引用段落图片表格 转义符 数学公式数学符号行内公式行间公式有公式计数器无公式计数器 公式包含文…

OpenGL3.3_C++_Windows(11)

git submodule项目子模块 Git Submodule (子模块的代码并不直接存储在父仓库中,而是通过一个指针来维护)克隆含有子模块的仓库时,使用git管理Git Clone (复制一份完整的Git仓库到本地)若仓库包含子模块&am…

MyBatis基础教程

文章目录 一、MyBatis基本使用1.1简介1.2搭建MyBatis环境1.2.1安装MyBatis1.2.2创建MyBatis核心配置文件1.2.3创建mapper接口1.2.4创建MyBatis映射文件1.2.5实现增加功能 1.3整合log4j1.4修改与删除功能1.5查询功能1.5.1查询单个实体类对象1.5.2查询所有用户信息 二、核心配置文…

C# Secs源码 HsmsSecs测试

包含客户端和服务端 启动客户端和服务端即可互相模拟sece 通讯 也可使用secs仿真器进行测试 开启后进行相关操作,创建客户端连接仿真器进行操作 仿真器显示日志 相关文件,源码 4.9 私信即可或者看我博客描述那个地址 我是狗子,希望你幸…

Vue47-修改默认配置webpack.config.js文件

main.js是脚手架项目的入口文件,系统运行时,默认去找src下的main.js文件。这是webpack通过配置文件:webpack.config.js配置的。 脚手架把所有重要的配置文件都隐藏了,方式被开发者修改。 一、查看被隐藏的webpack配置 1-1、webpa…

Matlab进阶绘图第60期—带伪彩图的曲面图

带伪彩图的曲面图是曲面图与伪彩图的组合。 其中,伪彩图与曲面图的颜色用于表示同一个特征。 由于伪彩图无遮挡但不直观,曲面图直观但有遮挡,而将二者组合,可以实现优势互补。 本期就来分享一下带伪彩图的曲面图的绘制方法&…

韩兴国/姜勇团队在《Trends in Plant Science》发表植物根系氮素再分配的观点文章!

氮素是陆地生态系统中的关键限制性营养元素,通过生物固氮和土壤氮供应通常远低高等植物的氮需求。当土壤氮素供应无法充分满足植物茎叶生长需求时,植物会通过自身营养器官(如根或根茎)再分配来实现氮的内部循环和再利用。尽管植物…

App端接口用例设计方法和测试方法

🍅 视频学习:文末有免费的配套视频可观看 🍅 点击文末小卡片 ,免费获取软件测试全套资料,资料在手,涨薪更快 前言 接口测试作为测试的重要一环,重点关注的是数据层面的输入输出,今天…

API接口设计的艺术:如何提升用户体验和系统性能

在数字时代,API接口的设计对于用户体验和系统性能有着至关重要的影响。良好的设计可以显著提升应用程序的响应速度、可靠性和易用性。以下是几个关键点,帮助改善API接口的设计: 1. 理解并定义清晰的要求 用户研究:与最终用户进行…

CesiumJS【Basic】- #006 浏览器控制台查看位置角度

文章目录 浏览器控制台查看位置角度1 目标 浏览器控制台查看位置角度 1 目标 浏览器控制台查看位置角度

详情资料SR560(斯坦福)SR570 低噪声前置放大器

SR560 低噪声前置放大器 SR560 是一款高性能、低噪声前置放大器,非常适合各种应用,包括低温测量、光学检测和音频工程。 输入 SR560 有一个差分前端,输入噪声为 4 nV/√Hz,输入阻抗为 100 MΩ。完整的噪声系数轮廓如下图所示。…

【笔记】复制Edge的网址粘贴后自动变成中文标题超链接

问题 1、从edge复制的网址粘贴直接显示网页内容名称而不是网址url。 2、复制任何网址粘贴到CSDN里面粘贴时直接转换成标题超链接(很讨厌的功能习惯)。 而如上两种问题不是互相影响的,就算设置了Edge的粘贴方式,复制到CSDN的文章…

【机器学习300问】120、该怎么用RNN来构建语言模型?

一、基本概念补充 在构建语言模型之前补充几个自然语言处理(NLP)基本概念。 (1)语料库(Corpus) ① 语料库的定义 在自然语言处理(NLP)领域,语料库是一个经过组织和加工…

自定义starter并发布maven私服

一、搭建nexus私服 nexus就是maven的私有服务器&#xff0c;这个搭建教程可以在网络上找到很多&#xff0c;这里就不赘述了。搭建完成之后再进行下一步 二、本地maven的setting配置文件中配置nexus的用户名和密码 <servers><server><id>nexus-releases<…

如何拥有自己的微信小程序

如何拥有自己的微信小程序 ~~话先放在这里~~ 写在前面申请一个属于自己的小程序先去[微信开放平台](https://open.weixin.qq.com/home)申请一个你的小程序扫码申请新小程序小程序该记好的个人信息 安装微信开发者工具下载工具关联你的小程序请求域名配置发布小程序 BUY一个自己…

抛光粉尘可爆性检测 打磨粉尘喷砂粉尘爆炸下限测试

抛光粉尘可爆性检测 抛光粉尘的可爆性检测是一种安全性能测试&#xff0c;用于确定加工过程中产生的粉尘在特定条件下是否会爆炸&#xff0c;从而对生产安全构成威胁。如果粉尘具有可爆性&#xff0c;那么在生产环境中就需要采取相应的防爆措施。粉尘爆炸的条件通常包括粉尘本身…

大模型应用开发技术:Multi-Agent框架流程、源码及案例实战(一)

LlaMA 3 系列博客 基于 LlaMA 3 LangGraph 在windows本地部署大模型 &#xff08;一&#xff09; 基于 LlaMA 3 LangGraph 在windows本地部署大模型 &#xff08;二&#xff09; 基于 LlaMA 3 LangGraph 在windows本地部署大模型 &#xff08;三&#xff09; 基于 LlaMA…