当前位置: 首页 > news >正文

C++设计模式之 依赖注入模式探索

依赖注入模式

  • 前言
  • 依赖注入模式的角色
  • 依赖注入模式的UML图
  • 依赖注入模式的设计和实现(C++)
  • 依赖注入和访问者模式的区别
  • 依赖注入模式的使用场景
  • 依赖注入模式的优缺点
  • 结语

前言

GoF设计模式主要关注的是面向对象编程设计的问题,而依赖注入作为一种编程技术,它的范畴更广泛,不仅适用于面向对象编程,还适用于其他编程范式。

依赖注入的核心思想是依赖反转原则(Dependency Inversion Principle, DIP),它是SOLID设计原则之一。依赖反转原则的核心观点是:高层模块不应该依赖于低层模块,它们都应该依赖于抽象。抽象不应该依赖于细节,细节应该依赖于抽象。

依赖注入通过将依赖关系从对象内部移到对象外部,使对象在运行时可以动态接收所依赖的对象。这样可以降低对象之间的耦合度,提高代码的可维护性和可测试性。依赖注入常见的实现方式有构造函数注入、setter方法注入和接口注入。

总之,虽然依赖注入不属于GoF的23种设计模式,但它仍然是一种重要的编程技术,被广泛应用于软件开发中。

依赖注入模式的角色

虽然依赖注入并不属于传统的设计模式,但我们仍然可以将其划分为以下几个角色:

  1. 依赖抽象(Abstraction of Dependency):这是一个抽象接口或抽象基类,它定义了一个组件所依赖的功能。高层模块和低层模块都依赖于这个抽象,而不是相互依赖。
  2. 依赖实现(Implementation of Dependency):这是实现依赖抽象的具体类。通常,一个依赖抽象可能有多个依赖实现,它们提供了不同的功能。
  3. 依赖消费者(Consumer of Dependency):这是依赖于依赖抽象的类,它通过依赖抽象与依赖实现进行交互。依赖消费者通常在运行时接收所依赖的具体实现,而不是在编译时直接依赖具体实现。这样做有助于降低耦合度,提高代码的可维护性和可测试性。
  4. 依赖注入器(Dependency Injector):这是负责创建和管理依赖实现的对象,以及将依赖实现注入到依赖消费者中的组件。依赖注入器可以是手动编写的代码,也可以是使用依赖注入容器(如 Spring、Google Guice)实现的自动化机制。

依赖注入的过程大致如下:

  1. 定义依赖抽象,以及针对不同场景的依赖实现。
  2. 在依赖消费者中引用依赖抽象,并通过构造函数、setter方法或接口注入的方式接收具体的依赖实现。
  3. 使用依赖注入器将合适的依赖实现注入到依赖消费者中。

通过这样的设计,您可以在不修改依赖消费者的前提下,灵活地调整依赖实现,从而提高代码的可维护性和可测试性。

依赖注入模式的UML图

下面是一个依赖注入的简化版UML类图。这里使用了一个音频播放器的例子,有一个抽象的AudioDecoder接口,以及Mp3DecoderWavDecoder这两个具体实现。AudioPlayer类是依赖消费者,它使用AudioDecoder接口播放音频。DependencyInjector类负责将合适的解码器实例注入到AudioPlayer类中。

+----------------+         +---------------------+
| AudioDecoder   |         | DependencyInjector  |
+----------------+         +---------------------+
| +decode(data)  |<--------| +injectDecoder()    |
+--------+-------+         +----------+----------+^                             ||                             |
+--------+-------+         +----------+----------+
| Mp3Decoder     |         | WavDecoder          |
+----------------+         +---------------------+
| +decode(data)  |         | +decode(data)       |
+----------------+         +---------------------+|                             ||                             |
+--------+-------+         +----------+----------+
| AudioPlayer    |         |                     |
+----------------+         +---------------------+
| -decoder       |-------->|                     |
| +play(data)    |         |                     |
+----------------+         +---------------------+

在这个例子中,AudioPlayer类依赖于AudioDecoder接口来播放音频。通过依赖注入,您可以在运行时将不同的解码器(如Mp3DecoderWavDecoder)注入到AudioPlayer类中,以实现对不同音频格式的支持。DependencyInjector类负责将合适的解码器实例注入到AudioPlayer类中。

这只是一个简化的UML类图,实际应用中可能会更加复杂。例如,您可以使用依赖注入容器来自动管理依赖关系,而无需手动编写DependencyInjector类。

依赖注入模式的设计和实现(C++)

在C++中,我们可以使用构造函数注入、setter方法注入或接口注入的方式实现依赖注入。下面是一个基于构造函数注入的例子,实现一个简单的消息服务:

  1. 定义依赖抽象(MessageService接口):
class MessageService {
public:virtual ~MessageService() = default;virtual void sendMessage(const std::string& message, const std::string& recipient) = 0;
};
  1. 实现依赖抽象(EmailServiceSMSService实现):
class EmailService : public MessageService {
public:void sendMessage(const std::string& message, const std::string& recipient) override {// 实现发送电子邮件的逻辑std::cout << "Sending email: " << message << " to " << recipient << std::endl;}
};class SMSService : public MessageService {
public:void sendMessage(const std::string& message, const std::string& recipient) override {// 实现发送短信的逻辑std::cout << "Sending SMS: " << message << " to " << recipient << std::endl;}
};
  1. 创建依赖消费者(Notification类):
class Notification {
public:Notification(std::shared_ptr<MessageService> messageService) : messageService_(messageService) {}void notify(const std::string& message, const std::string& recipient) {messageService_->sendMessage(message, recipient);}private:std::shared_ptr<MessageService> messageService_;
};
  1. main()函数中使用依赖注入:
int main() {// 创建依赖实现std::shared_ptr<MessageService> emailService = std::make_shared<EmailService>();std::shared_ptr<MessageService> smsService = std::make_shared<SMSService>();// 使用依赖注入创建Notification对象Notification emailNotification(emailService);Notification smsNotification(smsService);// 使用依赖消费者emailNotification.notify("Hello, world!", "user@example.com");smsNotification.notify("Hello, world!", "123-456-7890");return 0;
}

在这个例子中,我们使用构造函数注入的方式将依赖实现(EmailServiceSMSService)注入到依赖消费者(Notification类)中。通过这种方式,我们可以在不修改Notification类的前提下,灵活地调整消息发送的实现,提高代码的可维护性和可测试性。

注意:这个例子中并没有使用专门的依赖注入容器。在实际项目中,您可以使用现有的依赖注入库(如 Boost.DI)来管理依赖关系。

依赖注入和访问者模式的区别

依赖注入和访问者模式都是设计模式,用于解决不同的问题。它们有以下区别:

  1. 目的:
    • 依赖注入:依赖注入(Dependency Injection)主要用于解决对象之间的依赖关系。它可以将对象之间的依赖关系从对象内部移到对象外部,使得对象可以在运行时动态地接收所依赖的对象。这样做的目的是为了降低对象之间的耦合度,提高代码的可维护性和可测试性。
    • 访问者模式:访问者模式(Visitor Pattern)主要用于解决对象结构中元素的操作。它可以将不同类型的对象上的操作解耦,并将它们组织在一个访问者类中。这样做的目的是为了将操作与数据结构分离,使得在添加新的操作时无需修改数据结构,同时在添加新的数据结构时也无需修改操作。
  2. 使用场景:
    • 依赖注入:当对象之间存在依赖关系,但这些依赖关系需要在运行时动态地改变时,可以使用依赖注入。依赖注入可以使代码更易于测试,因为依赖关系可以通过测试框架提供的模拟对象来模拟。
    • 访问者模式:当一个对象结构包含多种类型的对象,且需要在这些对象上执行多种不同的操作时,可以使用访问者模式。访问者模式可以将这些操作从对象结构中分离出来,以实现更高的可扩展性和可维护性。
  3. 实现方式:
    • 依赖注入:依赖注入通常通过构造函数、属性(setter)或接口注入的方式将依赖关系注入到对象中。依赖注入容器(如:Spring, Google Guice)可以帮助管理这些依赖关系。
    • 访问者模式:访问者模式通过定义一个访问者接口,其中包含针对不同类型的对象的操作方法。对象结构的元素实现一个accept方法,用于接受访问者并调用相应的操作方法。

总之,依赖注入和访问者模式解决不同类型的问题,具有不同的目的和使用场景。依赖注入关注于对象之间的依赖关系,访问者模式关注于对象结构上的操作。

依赖注入模式的使用场景

依赖注入(Dependency Injection,DI)模式适用于以下场景:

  1. 解耦组件:依赖注入可以降低组件之间的耦合度,使组件依赖于抽象接口而不是具体实现。这使得组件在不影响其他部分的情况下能够更容易地进行修改和替换。当您有多个组件需要相互协作时,使用依赖注入可以有效地解耦组件,提高系统的可维护性。
  2. 单元测试:依赖注入可以简化单元测试,因为它允许您为依赖提供测试替代品(如mock对象或stub)。这使得您可以独立地测试组件,而无需关心依赖的具体实现。通过依赖注入,您可以更容易地编写可测试的代码,提高代码的可测试性。
  3. 代码重用:依赖注入使您可以将具体实现与消费者解耦,从而更容易地重用代码。例如,如果您有一个通用的数据访问层,您可以将其作为一个依赖注入到其他组件中,而无需关心它是如何实现的。这有助于提高代码重用和模块化。
  4. 配置灵活性:通过依赖注入,您可以在运行时更改组件的依赖关系,而无需重新编译或修改代码。这使得您可以更灵活地调整系统的行为,根据实际需要使用不同的实现。
  5. 控制反转(Inversion of Control, IoC):依赖注入是一种实现控制反转的技术。控制反转意味着将依赖关系的创建和管理从组件内部移动到外部,由依赖注入容器或其他外部实体来负责。这样可以降低代码的耦合度,提高代码的可维护性和可测试性。

综上所述,依赖注入模式在需要解耦组件、提高代码可测试性、重用和配置灵活性的场景中非常有用。

依赖注入模式的优缺点

依赖注入模式具有一定的优点和缺点。

优点:

  1. 解耦:依赖注入模式有助于降低组件之间的耦合度,使组件依赖于抽象接口而非具体实现。这有助于改善代码的可维护性,因为组件可以独立地修改和替换,而不会影响其他部分。
  2. 可测试性:依赖注入允许您为依赖提供测试替代品(如 mock 对象或 stub),使您可以独立地测试组件,而无需关心依赖的具体实现。这有助于编写可测试的代码,提高代码的可测试性。
  3. 配置灵活性:依赖注入允许您在运行时更改组件的依赖关系,而无需重新编译或修改代码。这使得您可以更灵活地调整系统的行为,根据实际需要使用不同的实现。
  4. 代码重用:通过将具体实现与消费者解耦,依赖注入可以帮助您更轻松地重用代码。您可以将通用功能作为依赖注入到其他组件中,而无需关心它是如何实现的。
  5. 控制反转(Inversion of Control, IoC):依赖注入是一种实现控制反转的技术。通过将依赖关系的创建和管理从组件内部移到外部,降低了代码的耦合度,提高了代码的可维护性和可测试性。

缺点:

  1. 学习曲线:对于初学者来说,理解和实现依赖注入可能需要一定的学习成本。特别是在使用依赖注入容器时,可能需要一定时间来学习和熟悉相关概念和技术。
  2. 代码复杂性:使用依赖注入可能会增加代码的复杂性,因为它引入了额外的抽象层。这可能会导致更多的接口和类,以及更复杂的代码结构。
  3. 过度工程:在某些情况下,使用依赖注入可能会导致过度工程。对于一些简单的应用程序,直接使用具体实现可能就足够了,而引入依赖注入可能并不会带来明显的优势。

总之,依赖注入模式在许多情况下都是非常有用的,但需要权衡其优缺点,根据具体场景和需求来决定是否使用。在需要解耦、提高可测试性和配置灵活性的场景中,依赖注入模式的优点可能会超过其缺点。然而,在简单的应用程序中,引入依赖注入可能会导致不必要的复杂性和过度工程。

为了充分利用依赖注入模式的优势,您需要了解如何在适当的场景中使用它。以下是一些建议,可以帮助您在实践中更好地应用依赖注入模式:

  1. 合理划分模块和组件:依赖注入模式依赖于模块化的代码结构,所以在引入依赖注入之前,请确保您的应用程序具有合理的模块和组件划分。这将有助于减轻复杂性,并确保您能够充分利用依赖注入带来的好处。
  2. 选择合适的依赖注入方式:依赖注入有多种实现方法,如构造函数注入、setter方法注入和接口注入。根据您的具体需求和场景选择合适的方式。例如,构造函数注入通常更适用于需要在对象创建时就注入依赖的场景,而setter方法注入可能更适合于依赖可能在运行时发生变化的情况。
  3. 使用依赖注入容器:在复杂的应用程序中,使用依赖注入容器(如Boost.DI)可以简化依赖管理,提高代码的可维护性和可读性。依赖注入容器通常提供更高级的功能,如自动注入和生命周期管理,这可以帮助您更轻松地实现和管理依赖关系。
  4. 适度使用抽象:虽然依赖注入模式依赖于抽象接口,但这并不意味着您需要为每一个组件创建抽象接口。在一些情况下,直接使用具体实现可能更简单、更直接。您需要根据具体场景来权衡抽象程度,以确保代码保持清晰和简洁。
  5. 考虑性能影响:依赖注入模式通常会引入额外的间接性和运行时开销。虽然在大多数情况下,这种开销可能是可以接受的,但在性能敏感的应用程序中,您需要考虑这种影响,并根据需要进行优化。例如,您可以考虑使用更轻量级的依赖注入框架,或者在关键性能路径上避免使用依赖注入。

结语

依赖注入模式具有一定的优点和缺点,但从心理学的角度来看,它符合人类在处理复杂问题时的认知特点。通过解耦组件和使用抽象接口,依赖注入模式可以帮助我们更好地管理和维护代码。当我们面对复杂的软件系统时,人们往往善于处理具有清晰结构和逻辑关系的问题。依赖注入正是利用了这一特点,将复杂的依赖关系简化为更容易理解和处理的形式。

学习和运用依赖注入模式需要一定的努力,但正如心理学研究所揭示的那样,我们具备自我调整和适应新技能的能力。在掌握依赖注入模式的过程中,不妨从实际需求出发,通过案例分析和实践应用来积累经验。在实际项目中运用依赖注入模式,您将更好地领会到它为代码解耦、提高可测试性和可维护性带来的好处。

如果您觉得这篇关于依赖注入模式的介绍对您有所帮助,希望您能收藏和点赞,以便更多的人了解并学习这一有益的设计模式。让我们一起努力,通过学习和运用先进的设计模式,使我们的代码变得更加健壮、灵活和优雅。

相关文章:

C++设计模式之 依赖注入模式探索

依赖注入模式 前言依赖注入模式的角色依赖注入模式的UML图依赖注入模式的设计和实现(C)依赖注入和访问者模式的区别依赖注入模式的使用场景依赖注入模式的优缺点结语 前言 GoF设计模式主要关注的是面向对象编程设计的问题&#xff0c;而依赖注入作为一种编程技术&#xff0c;它…...

如何实现Spring AOP以及Spring AOP的实现原理

AOP:面向切面编程,它和OOP&#xff08;面向对象编程)类似。 AOP组成: 1、切面:定义AOP是针对那个统一的功能的&#xff0c;这个功能就叫做一个切面&#xff0c;比如用户登录功能或方法的统计日志&#xff0c;他们就各种是一个切面。切面是有切点加通知组成的。 2、连接点:所有可…...

数学建模——数据预处理

在数学建模时&#xff0c;经常遇到数据的预处理&#xff0c;那么会有一些什么情况呢&#xff0c;跟着北海老师总结了他的内容~希望对大家有所帮助&#xff01; 缺失值 比赛提供的数据&#xff0c;发现有些单元格是null或空的缺失太多:例如调查人口信息&#xff0c;发现“年龄…...

第8章:树

1.树是什么 一种分层数据的抽象模型前端工作中常见的树包括&#xff1a;DOM树&#xff0c;级联选择(省市区)&#xff0c;树形控件&#xff0c;…javascript中没有树&#xff0c;但是可以用Object和Array构建树 4.树的常用操作&#xff1a;深度/广度优先遍历&#xff0c;先中后…...

Java基础学习(10)

Java基础学习 一、JDK8时间类1.1 Zoneld时区1.2 Instant时间戳1.3 ZonedDateTime1.4 DateTimeFormatter1.5 日历类时间表示1.6 工具类1.7 包装类JDK5提出的新特性Integer成员方法 二、集合进阶2.1 集合的体系结构2.1.1 Collection 2.2collection的遍历方式2.2.1 迭代器遍历2.2.…...

Tomcat多实例部署实验

引言 本文主要内容是tomcat的多实例配置实验。 一、实验准备 Tomcat多实例是指在一台设备上运行多个Tomcat服务&#xff0c;这些Tomcat相互独立&#xff0c;互不影响。多实例与虚拟主机不同&#xff0c;虚拟主机的本质是在一个服务下有多个相对独立的目录&#xff0c;但是多实…...

无良公司把我从上家挖过来,白嫖了六个月,临近试用期结束才说不合适,催我赶紧找下家!...

职场套路多&#xff0c;一不小心就会掉坑&#xff0c;一位网友讲述了自己的遭遇&#xff1a; 今天被领导催促离职了&#xff0c;当时就是这个领导把他从别的公司挖过来。这家公司催得太急&#xff0c;为了投奔这里&#xff0c;他和上家的HR都闹翻了&#xff0c;上家总监挽留他&…...

忙碌中也要记得休息,这两款好玩的游戏推荐给你

第一款&#xff1a;古墓丽影9年度版 《古墓丽影9》&#xff08;原名Tomb Raider&#xff09;是由水晶动力开发&#xff0c;史克威尔艾尼克斯发行的动作冒险游戏。 它于 2013 年发布。续集是古墓丽影崛起和古墓丽影暗影。 本作的重点是新版劳拉&#xff08;Lara Croft&#xf…...

四种方法可以实现判断字符串包含某个字符

小编介绍过js中使用indexOf() 方法判断字符串包含某个字是一个很好用的方法&#xff0c;但除了这个方法之外&#xff0c;JavaScript中还有四种方法可以实现判断字符串包含某个字符&#xff1a; 1、使用字符串search() 方法 search() 方法用于检索字符串中指定的子字符串&…...

ubuntu进程相关command

列出当前系统中所有正在运行的进程的详细信息 ps aux查看所有包含某关键字的进程 例&#xff1a;查看所有包含关键字click的进程 ps aux | grep click运行后显示如下信息&#xff1a; root 8998 0.0 0.0 10984 4052 ? S 4月23 0:00 sudo ./bin/click…...

7.参数校验

在controller和service进行前端传参校验&#xff0c;保证存到数据库的数据是正确的 1.引入依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-validation</artifactId></dependency>这里无需…...

nginx简单介绍

文章目录 1. 下载并解压2. 80端口被占用&#xff0c;更改nginx默认的监听端口3. 访问nginx4. 在linux上安装nginx5. nginx常用命令6. nginx.conf 1. 下载并解压 官网下载 2. 80端口被占用&#xff0c;更改nginx默认的监听端口 更改conf/nginx.conf文件 3. 访问nginx ht…...

美创科技首届渠道高峰论坛| 两大分论坛亮点汇聚

4月22日&#xff0c;美创科技首届渠道高峰论坛在海南三亚隆重举行&#xff0c;本届高峰论坛以“新起点 新战略 共赢数安蓝海”为主题&#xff0c;全国各地200余家合作伙伴齐聚。当日下午&#xff0c;行业分论坛、技术分论坛两大论坛以及圆桌会议&#xff0c;多方视角、全方位共…...

QML中【预计符号】和【Unknown Component M300】的红色警告解决方法

问题描述&#xff1a; QML的项目中带中文&#xff0c;每次打开项目都在问题栏显示【预计符号】的红色警告&#xff0c;还有一种是【Unknown Component M300】的警告&#xff0c;代码能正常编译和运行。像我这样对代码追求优雅的强迫症患者看着很不爽&#xff0c;查了很多网上的…...

聊聊「低代码」的实践之路

区块链、低代码、元宇宙、AI智能&#xff1b; 01 【先来说说背景】 这个概念由来已久&#xff0c;但是在国内兴起&#xff0c;是最近几年&#xff1b; 低代码即「Low-Code」&#xff1b; 指提供可视化开发环境&#xff0c;可以用来创建和管理软件应用&#xff1b; 简单的说…...

(一)服务发现组件 Eureka

1、Eureka 简介 Eureka 是Spring Cloud Netflix 微服务套件中的一部分&#xff0c; 它基于Netflix Eureka 做了二次封装&#xff0c; 主要负责完成微服务架构中的服务治理功能。我们只需通过简单引入依赖和注解配置就能让Spring Boot 构建的微服务应用轻松地与Eureka 服务治理…...

学会笔记本电脑录屏快捷键,轻松实现录屏!

案例&#xff1a;笔记本电脑录屏有快捷键吗&#xff1f; 【我每次打开笔记本电脑录屏都要耗费比较长的时间&#xff0c;这样会影响到我录屏的效率。在这里想问一下&#xff0c;有没有快速打开电脑录屏的方法&#xff1f;】 在日常的工作、学习、娱乐中&#xff0c;我们经常需…...

( “树” 之 Trie) 208. 实现 Trie (前缀树) ——【Leetcode每日一题】

知识点回顾 &#xff1a; Trie&#xff0c;又称前缀树或字典树&#xff0c;用于判断字符串是否存在或者是否具有某种字符串前缀。 ❓208. 实现 Trie (前缀树) 难度&#xff1a;中等 Trie&#xff08;发音类似 “try”&#xff09;或者说 前缀树 是一种树形数据结构&#xff…...

算法训练Day40:343. 整数拆分 96.不同的二叉搜索树

文章目录 整数拆分题解&#xff08;动态规划&#xff09;贪心 不同的二叉搜索树题解 整数拆分 CategoryDifficultyLikesDislikesContestSlugProblemIndexScorealgorithmsMedium (62.22%)11660--0 Tags 数学 | 动态规划 Companies 给定一个正整数 n &#xff0c;将其拆分为…...

设计模式及代码

1、工厂方法模式&#xff08;Factory Method Pattern&#xff09;&#xff1a; 定义一个用于创建对象的接口&#xff0c;让子类决定实例化哪一个类。应用场景&#xff1a;当一个类不知道它所必须创建的对象的类时&#xff1b;一个类希望由它的子类来指定它所创建的对象时。 抽…...

RestClient

什么是RestClient RestClient 是 Elasticsearch 官方提供的 Java 低级 REST 客户端&#xff0c;它允许HTTP与Elasticsearch 集群通信&#xff0c;而无需处理 JSON 序列化/反序列化等底层细节。它是 Elasticsearch Java API 客户端的基础。 RestClient 主要特点 轻量级&#xff…...

微信小程序之bind和catch

这两个呢&#xff0c;都是绑定事件用的&#xff0c;具体使用有些小区别。 官方文档&#xff1a; 事件冒泡处理不同 bind&#xff1a;绑定的事件会向上冒泡&#xff0c;即触发当前组件的事件后&#xff0c;还会继续触发父组件的相同事件。例如&#xff0c;有一个子视图绑定了b…...

可靠性+灵活性:电力载波技术在楼宇自控中的核心价值

可靠性灵活性&#xff1a;电力载波技术在楼宇自控中的核心价值 在智能楼宇的自动化控制中&#xff0c;电力载波技术&#xff08;PLC&#xff09;凭借其独特的优势&#xff0c;正成为构建高效、稳定、灵活系统的核心解决方案。它利用现有电力线路传输数据&#xff0c;无需额外布…...

【HTML-16】深入理解HTML中的块元素与行内元素

HTML元素根据其显示特性可以分为两大类&#xff1a;块元素(Block-level Elements)和行内元素(Inline Elements)。理解这两者的区别对于构建良好的网页布局至关重要。本文将全面解析这两种元素的特性、区别以及实际应用场景。 1. 块元素(Block-level Elements) 1.1 基本特性 …...

SAP学习笔记 - 开发26 - 前端Fiori开发 OData V2 和 V4 的差异 (Deepseek整理)

上一章用到了V2 的概念&#xff0c;其实 Fiori当中还有 V4&#xff0c;咱们这一章来总结一下 V2 和 V4。 SAP学习笔记 - 开发25 - 前端Fiori开发 Remote OData Service(使用远端Odata服务)&#xff0c;代理中间件&#xff08;ui5-middleware-simpleproxy&#xff09;-CSDN博客…...

HarmonyOS运动开发:如何用mpchart绘制运动配速图表

##鸿蒙核心技术##运动开发##Sensor Service Kit&#xff08;传感器服务&#xff09;# 前言 在运动类应用中&#xff0c;运动数据的可视化是提升用户体验的重要环节。通过直观的图表展示运动过程中的关键数据&#xff0c;如配速、距离、卡路里消耗等&#xff0c;用户可以更清晰…...

音视频——I2S 协议详解

I2S 协议详解 I2S (Inter-IC Sound) 协议是一种串行总线协议&#xff0c;专门用于在数字音频设备之间传输数字音频数据。它由飞利浦&#xff08;Philips&#xff09;公司开发&#xff0c;以其简单、高效和广泛的兼容性而闻名。 1. 信号线 I2S 协议通常使用三根或四根信号线&a…...

windows系统MySQL安装文档

概览&#xff1a;本文讨论了MySQL的安装、使用过程中涉及的解压、配置、初始化、注册服务、启动、修改密码、登录、退出以及卸载等相关内容&#xff0c;为学习者提供全面的操作指导。关键要点包括&#xff1a; 解压 &#xff1a;下载完成后解压压缩包&#xff0c;得到MySQL 8.…...

用神经网络读懂你的“心情”:揭秘情绪识别系统背后的AI魔法

用神经网络读懂你的“心情”:揭秘情绪识别系统背后的AI魔法 大家好,我是Echo_Wish。最近刷短视频、看直播,有没有发现,越来越多的应用都开始“懂你”了——它们能感知你的情绪,推荐更合适的内容,甚至帮客服识别用户情绪,提升服务体验。这背后,神经网络在悄悄发力,撑起…...

UE5 音效系统

一.音效管理 音乐一般都是WAV,创建一个背景音乐类SoudClass,一个音效类SoundClass。所有的音乐都分为这两个类。再创建一个总音乐类&#xff0c;将上述两个作为它的子类。 接着我们创建一个音乐混合类SoundMix&#xff0c;将上述三个类翻入其中&#xff0c;通过它管理每个音乐…...