C++笔记之设计模式:setter函数、依赖注入

C++笔记之设计模式:setter函数、依赖注入

参考笔记:
1.C++笔记之静态成员函数可以在类外部访问私有构造函数吗?
2.C++笔记之设计模式:setter函数、依赖注入
3.C++笔记之两个类的实例之间传递参数——通过构造函数传递类对象的方法详细探究
4.C++笔记之智能指针和单例、依赖注入结合使用
5.C++笔记之两个类的实例之间传递参数的各种方法

请添加图片描述

code review!

文章目录

  • C++笔记之设计模式:setter函数、依赖注入
    • 1.概念
    • 2.基本示例
    • 3.setter函数
    • 4.基本示例+setter函数构成依赖注入
    • 5.概念——ChatGpt
    • 6.构造函数注入示例
    • 7.接口注入示例
    • 8. 构造函数注入的使用场景和用途
    • 9.接口注入的使用场景和用途
    • 10.使用一个简单的业务逻辑来演示构造函数注入和接口注入的使用场景。
      • 10.1.构造函数注入示例
      • 10.2.接口注入示例
    • 11.附加知识:SOLID设计原则
    • 12.附加知识:依赖倒置原则
    • 13.一个图形绘制应用程序。我们将创建一个图形类(Shape),并通过依赖注入来注入不同的绘制器(Drawer)来绘制不同的图形。

1.概念

依赖注入(Dependency Injection,DI)是一种软件设计模式,用于管理和解决组件之间的依赖关系。在传统的编程中,一个对象通常需要在自身内部创建或获取其所依赖的其他对象,这可能会导致紧密耦合的代码,使得代码难以测试、难以维护和难以扩展。依赖注入的目标是通过从外部传递依赖项来解耦组件,提高代码的可测试性、可维护性和灵活性。

依赖注入的主要思想是,一个组件(被称为"受注入对象")不应该负责创建或获取其依赖的对象。相反,这些依赖应该由外部实例化,并通过构造函数、方法参数、属性等方式注入到受注入对象中。这种方式可以在不修改受注入对象的情况下,灵活地替换依赖项的具体实现,以及在测试时传递模拟对象。

依赖注入可以分为以下几种形式:

  1. 构造函数注入:通过构造函数将依赖项传递给受注入对象。这是最常见的依赖注入方式。通过构造函数,依赖关系在对象创建时就被传递,并在整个对象生命周期中保持稳定。

  2. 方法参数注入:通过方法的参数将依赖项传递给对象的方法。这在需要特定依赖项执行特定操作的情况下非常有用。

  3. 属性注入:通过公开的属性将依赖项传递给对象。这种方式可能导致依赖关系的意外更改,因此在使用时需要小心。

  4. 接口注入:通过接口或抽象类定义依赖项,然后将具体实现传递给对象。这种方式允许在运行时替换依赖项的具体实现,实现多态性。

依赖注入的优势包括:

  • 解耦和灵活性:减少了组件之间的紧耦合,使得代码更加灵活、可维护和易于扩展。

  • 可测试性:可以轻松地传递模拟对象或桩对象,以进行单元测试,从而提高代码的测试覆盖率。

  • 可读性:依赖关系在代码中被明确地传递,使代码更易于理解。

简单理解来说就是当依赖的某个对象是通过外部来注入,而不是自己创建。

2.基本示例

在这里插入图片描述

3.setter函数

在这里插入图片描述

4.基本示例+setter函数构成依赖注入

在这里插入图片描述

代码

class Tools {
public:virtual void doWork() = 0;
};class Hammer : public Tools {
public:void doWork() override {std::cout << "use hammer" << std::endl;}
};class Axe : public Tools {
public:void doWork() override {std::cout << "use Axe" << std::endl;}
};class Human {
public:Human(Tools& t) : tools(t) {}void doWork() {tools.doWork();}Tools& tools;
};void MakeHuman() {Hammer hammer;Human human1(hammer);human1.doWork();Axe axe;Human human2(axe);human2.doWork();
}int main() {MakeHuman();return 0;
}

5.概念——ChatGpt

在这里插入图片描述

6.构造函数注入示例

在这里插入图片描述

代码

#include <iostream>class Dependency {
public:void DoSomething() {std::cout << "Dependency is doing something." << std::endl;}
};class Service {
public:Service(Dependency* dependency) : dependency_(dependency) {}void UseDependency() {std::cout << "Service is using dependency." << std::endl;dependency_->DoSomething();}private:Dependency* dependency_;
};int main() {Dependency dependency;Service service(&dependency);service.UseDependency();return 0;
}

7.接口注入示例

在这里插入图片描述

代码

#include <iostream>class IDependency {
public:virtual void DoSomething() = 0;
};class Dependency : public IDependency {
public:void DoSomething() override {std::cout << "Dependency is doing something." << std::endl;}
};class Service {
public:Service(IDependency* dependency) : dependency_(dependency) {}void UseDependency() {std::cout << "Service is using dependency." << std::endl;dependency_->DoSomething();}private:IDependency* dependency_;
};int main() {Dependency dependency;Service service(&dependency);service.UseDependency();return 0;
}

8. 构造函数注入的使用场景和用途

构造函数注入是一种通过类的构造函数将依赖项传递给类的方式。这种方式适用于以下情况:

  • 类需要一个稳定的依赖关系:通过构造函数注入,依赖项在对象创建时被设置,然后在整个对象生命周期内保持不变,确保了稳定的依赖关系。

  • 松耦合和可测试性:依赖项的传递通过构造函数进行,使得类与具体的依赖实现解耦,从而提高了代码的可测试性和可维护性。

  • 代码可读性:通过构造函数明确地传递依赖项,使得类的依赖关系更加明确和清晰。

  • 依赖的外部控制:通过构造函数注入,外部代码可以在创建对象时决定传递的依赖项,从而实现对依赖的更好控制。

9.接口注入的使用场景和用途

接口注入是通过使用接口或抽象类定义依赖关系,然后传递具体实现的方式。这种方式适用于以下情况:

  • 多态性和扩展性:接口注入允许在运行时决定使用的具体实现,从而实现多态性。这对于在不修改现有代码的情况下扩展应用程序非常有用。

  • 模块替换:通过传递不同的实现,可以轻松替换具体的依赖项,从而实现模块的替换和重用。

  • 依赖反转:通过依赖接口而不是具体实现,实现了依赖反转的原则,即高层模块不依赖于低层模块的具体实现细节。

  • 解耦和灵活性:接口注入减少了类之间的紧耦合,从而提高了代码的灵活性和可维护性。

总之,构造函数注入和接口注入都是实现依赖注入的有效方式,可以根据项目需求选择适当的方式。构造函数注入适用于稳定的依赖关系和明确的依赖项传递,而接口注入适用于多态性、扩展性和模块替换的需求。无论使用哪种方式,依赖注入的目标是减少紧耦合,提高代码的可测试性、可维护性和灵活性。

10.使用一个简单的业务逻辑来演示构造函数注入和接口注入的使用场景。

我们将创建一个模拟的报告生成系统,其中包括一个报告生成器和一个数据源。
业务逻辑:
我们有一个报告生成器(ReportGenerator),它依赖于一个数据源(DataSource)。报告生成器通过数据源获取数据,并生成报告。

10.1.构造函数注入示例

在这里插入图片描述

代码

#include <iostream>
#include <string>// 数据源类,提供获取数据的方法
class DataSource {
public:std::string GetData() {return "Data from the data source.";}
};// 报告生成器类,依赖于数据源
class ReportGenerator {
public:// 构造函数,接收一个数据源对象作为依赖ReportGenerator(DataSource* dataSource) : dataSource_(dataSource) {}// 生成报告的方法void GenerateReport() {// 使用数据源获取数据std::string data = dataSource_->GetData();std::cout << "Generating report with data: " << data << std::endl;}private:DataSource* dataSource_;  // 数据源对象的指针
};int main() {DataSource dataSource;  // 创建数据源对象ReportGenerator reportGenerator(&dataSource);  // 创建报告生成器,并传入数据源对象reportGenerator.GenerateReport();  // 生成报告return 0;
}

10.2.接口注入示例

在这里插入图片描述

代码

#include <iostream>
#include <string>// 数据源接口,定义获取数据的纯虚方法
class IDataSource {
public:virtual std::string GetData() = 0;
};// 数据源类,实现数据源接口,提供获取数据的方法
class DataSource : public IDataSource {
public:std::string GetData() override {return "Data from the data source.";}
};// 报告生成器类,依赖于数据源接口
class ReportGenerator {
public:// 构造函数,接收一个数据源接口的指针作为依赖ReportGenerator(IDataSource* dataSource) : dataSource_(dataSource) {}// 生成报告的方法void GenerateReport() {// 使用数据源接口获取数据std::string data = dataSource_->GetData();std::cout << "Generating report with data: " << data << std::endl;}private:IDataSource* dataSource_;  // 数据源接口的指针
};int main() {DataSource dataSource;  // 创建数据源对象ReportGenerator reportGenerator(&dataSource);  // 创建报告生成器,并传入数据源对象reportGenerator.GenerateReport();  // 生成报告return 0;
}

11.附加知识:SOLID设计原则

SOLID是一组五个面向对象编程和设计的原则,旨在帮助开发者创建更加可维护、灵活和可扩展的软件。这些原则是:

  1. 单一职责原则(Single Responsibility Principle,SRP)
    每个类应该有且仅有一个引起变化的原因,即一个类应该只负责一项职责。这有助于将类的职责分离,使代码更加模块化,提高可维护性和可测试性。

  2. 开放封闭原则(Open/Closed Principle,OCP)
    软件实体(类、模块、函数等)应该对扩展开放,对修改关闭。这意味着在不修改现有代码的情况下,可以通过添加新代码来扩展功能。这有助于减少影响已有功能的风险。

  3. 里氏替换原则(Liskov Substitution Principle,LSP)
    子类必须能够替换其基类,而不影响程序的正确性。这意味着子类应该能够保持基类的行为,并且不应该破坏基类的约定。

  4. 接口隔离原则(Interface Segregation Principle,ISP)
    不应该强迫客户端(使用接口的类)依赖于它们不需要的接口。这个原则鼓励将大型接口拆分成更小、更具体的接口,以便客户端只需实现它们所需的部分。

  5. 依赖倒置原则(Dependency Inversion Principle,DIP)
    高层模块不应该依赖于低层模块,两者都应该依赖于抽象。抽象不应该依赖于细节,细节应该依赖于抽象。这个原则强调通过依赖于抽象来实现解耦和灵活性。

这些原则共同构成了SOLID原则,它们的目标是指导开发者编写更加灵活、可扩展和易于维护的代码。这些原则并不是僵硬的规则,而是一种指导思想,根据实际情况和项目需求进行适当的应用和权衡。

12.附加知识:依赖倒置原则

依赖倒置原则(Dependency Inversion Principle,DIP)是SOLID设计原则中的一部分,提出了一种关于如何设计和组织软件架构的指导思想。DIP的核心思想是:

  1. 高层模块不应该依赖于低层模块。两者都应该依赖于抽象。
  2. 抽象不应该依赖于细节。细节应该依赖于抽象。

这个原则强调了以下几个关键概念:

  • 高层模块:指的是较高层次的组件、模块或类,它们通常是实现业务逻辑的部分。

  • 低层模块:指的是较低层次的组件、模块或类,它们通常是实现底层细节的部分,比如工具类、数据库访问等。

  • 抽象:指的是接口、抽象类或其他形式的抽象层,用于定义高层和低层之间的通信接口。

  • 细节:指的是具体的实现细节,通常包括具体类、方法等。

依赖倒置原则的目标是减少组件之间的紧耦合,提高系统的灵活性、可维护性和可扩展性。通过将高层模块和低层模块都依赖于抽象,可以实现以下几个好处:

  1. 可替换性:高层模块可以轻松地切换不同的低层实现,而不需要修改高层代码。

  2. 可测试性:通过依赖于抽象,可以更容易地进行单元测试,以及在测试中使用模拟对象或桩对象。

  3. 模块解耦:高层模块和低层模块之间的关系由抽象定义,减少了紧密的依赖关系。

  4. 灵活性:系统更容易适应变化,因为只需要修改抽象或新的低层实现,而不需要修改高层模块。

在之前提供的示例中,使用接口注入的方式就体现了依赖倒置原则。通过依赖于抽象的接口(或者基类),高层模块和低层模块之间的关系由抽象定义,达到了解耦和灵活性的目标。

13.一个图形绘制应用程序。我们将创建一个图形类(Shape),并通过依赖注入来注入不同的绘制器(Drawer)来绘制不同的图形。

在这里插入图片描述

代码

#include <iostream>
#include <string>// 抽象绘制器接口
class Drawer {
public:virtual void Draw(const std::string& shapeType) = 0;
};// 具体的绘制器实现
class ConsoleDrawer : public Drawer {
public:void Draw(const std::string& shapeType) override {std::cout << "Drawing " << shapeType << " on console." << std::endl;}
};class FileDrawer : public Drawer {
public:void Draw(const std::string& shapeType) override {std::cout << "Drawing " << shapeType << " in file." << std::endl;}
};// 图形类,依赖于绘制器接口
class Shape {
public:Shape(Drawer* drawer) : drawer_(drawer) {}void Draw(const std::string& shapeType) {drawer_->Draw(shapeType);}private:Drawer* drawer_;
};int main() {ConsoleDrawer consoleDrawer;FileDrawer fileDrawer;Shape circle(&consoleDrawer);Shape rectangle(&fileDrawer);circle.Draw("circle");rectangle.Draw("rectangle");return 0;
}

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【C++】进一步认识模板

&#x1f3d6;️作者&#xff1a;malloc不出对象 ⛺专栏&#xff1a;C的学习之路 &#x1f466;个人简介&#xff1a;一名双非本科院校大二在读的科班编程菜鸟&#xff0c;努力编程只为赶上各位大佬的步伐&#x1f648;&#x1f648; 目录 前言一、非类型模板参数二、模板的特…

LeetCode面试经典150题(day 3)

169. 多数元素 难度&#xff1a;简单 给定一个大小为 n 的数组 nums &#xff0c;返回其中的多数元素。多数元素是指在数组中出现次数 大于 ⌊ n/2 ⌋ 的元素。 你可以假设数组是非空的&#xff0c;并且给定的数组总是存在多数元素。 示例 1&#xff1a; 输入&#xff1a;nums …

java八股文面试[数据结构]——HashMap扩容优化

知识来源&#xff1a; 【2023年面试】HashMap在扩容上做了哪些优化_哔哩哔哩_bilibili

kubernetes--技术文档-真--集群搭建-三台服务器一主二从(非高可用)-三服务器位于同交换机中

在使用k8s之前如果不太熟悉k8s的可以先看这个文章&#xff1a; kubernetes--技术文档--基本概念--《10分钟快速了解》_一单成的博客-CSDN博客 三节点相同安装操作&#xff1a; 1、设置hosts解析 根据角色在三个服务器中运行&#xff0c;设置自己的hostname。 标识&#xf…

分布式 - 服务器Nginx:一小时入门系列之 return 指令

文章目录 1. return 指令语法2. return code URL 示例3. return code text 示例4. return URL 示例 1. return 指令语法 return指令用于立即停止当前请求的处理&#xff0c;并返回指定的HTTP状态码和响应头信息&#xff0c;它可以用于在Nginx中生成自定义错误页面&#xff0c;…

设计模式(11)观察者模式

一、概述&#xff1a; 1、定义&#xff1a;观察者模式定义了一种一对多的依赖关系&#xff0c;让多个观察者对象同时监听某一个主题对象。这个主题对象在状态发生变化时,会通知所有观察者对象&#xff0c;使它们能够自动更新自己。 2、结构图&#xff1a; public interface S…

[管理与领导-50]:IT基层管理者 - 8项核心技能 - 5 - 沟通是润滑剂

目录 前言&#xff1a; 一、什么是沟通 1.1 定义 1.2 沟通模型 1.3 沟通的六层次模型 1.4 为什么需要沟通 二、沟通的五维度 三、沟通的原则 3.1 以终为始 3.2 双赢思维&#xff1a;人们只会做对自己有利的事 3.3 牵善的思维 四、沟通的过程 五、沟通技巧 六、深…

mysql数据库root密码遗忘后,修改root密码

目录 方式一&#xff1a; 方式二&#xff1a; 2.1 也可以像我这样&#xff0c;普通用户登录进去后 2.2 执行如下命令&#xff0c;将已知的user1的加密密文更新到root中 2.3 查询数据库 2.4 用root用户登录 2.5 登录正常&#xff0c;但这会root登录进去后&#xff0c;无法…

代码随想录算法训练营第四十六天|LeetCode 1143,1035,53

目录 LeetCode 1143.最长公共子序列 动态规划五步曲&#xff1a; 1.确定dp[i][j]的含义 2.找出递推公式 3.初始化dp数组 4.确定遍历顺序 5.打印dp数组 LeetCode 1035.不相交的线 LeetCode 53.最大子序列和&#xff08;动态规划&#xff09; 动态规划五步曲&#xff1a; 1.确定…

深度学习11:Transformer

目录 什么是 Transformer&#xff1f; Encoder Decoder Attention Self-Attention Context-Attention 什么是 Transformer&#xff08;微软研究院笨笨&#xff09; RNN和Transformer区别 Universal Transformer和Transformer 区别 什么是 Transformer&#xff1f; ​ …

基于MATLAB开发AUTOSAR软件应用层Code mapping专题-part 2 Inport和Outports 标签页介绍

上篇我们介绍了Function页的内容,这篇我们介绍Inports和Outports页的内容,这里我们再次强调一个概念,code mapping是以simulink的角度去看的,就是先要在模型中建立simulink模块,在code mapping里映射他要对应的autosar的元素,之后生成代码时的c语言的名字是以Autosar的元…

机器学习在大数据分析中的应用

文章目录 机器学习在大数据分析中的原理机器学习在大数据分析中的应用示例预测销售趋势客户细分和个性化营销 机器学习在大数据分析中的前景和挑战前景挑战 总结 &#x1f389;欢迎来到AIGC人工智能专栏~探索机器学习在大数据分析中的应用 ☆* o(≧▽≦)o *☆嗨~我是IT陈寒&…

项目进度管理(4-2)关键链法和关键路径法的区别和联系

1 关键链法和关键路径法的主要区别 1.1 关键链法和关键路径法的关注焦点不同 关键路径法&#xff08;CPM&#xff09;&#xff1a;关注项目中最长的路径&#xff0c;也就是所需时间最长的路径&#xff0c;这被称为关键路径。关键路径决定了项目的最早完成时间。关键链法&…

2023 百度翻译 爬虫 js逆向 代码

js代码&#xff1a; const jsdom require("jsdom"); const {JSDOM} jsdom; const dom new JSDOM(<!DOCTYPE html><p>Hello world</p>); window dom.window; document window.document; XMLHttpRequest window.XMLHttpRequest;function n(t,…

java开发之fastjson

依赖 <!-- fastjson依赖 --> <!-- https://mvnrepository.com/artifact/com.alibaba/fastjson --> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.76</version> <…

vue ui 创建项目没有反应

问题 cmd中输入 vue ui 没有反应 解决办法 vue ui命令需要vue3.0以上的版本才可以 1、查看当前版本 vue --version vue版本在3.0以下是没有ui命令的 2、查看版本所拥有的命令 vue -h 3、卸载之前版本的vue npm uninstall vue-cli -g 卸载完成&#xff0c;检查是否已经…

尚硅谷宋红康MySQL笔记 10-18

是记录&#xff0c;我不会记录的特别详细 第10章 创建和管理表 标识符命名规则 数据库名、表名不得超过30个字符&#xff0c;变量名限制为29个只能包含 A–Z, a–z, 0–9, _共63个字符数据库名、表名、字段名等对象名中间不要包含空格同一个MySQL软件中&#xff0c;数据库不能…

群晖 NAS WebDAV服务手机ES文件浏览器远程访问【无公网IP内网穿透】

&#x1f4f1; iOS开发上架主页 在强者的眼中&#xff0c;没有最好&#xff0c;只有更好。我们是移动开发领域的优质创作者&#xff0c;同时也是阿里云专家博主。 ✨ 关注我们的主页&#xff0c;探索iOS开发的无限可能&#xff01; &#x1f525;我们与您分享最新的技术洞察和实…

QT5.12.12通过ODBC连接到GBase 8s数据库(CentOS)

本示例使用的环境如下&#xff1a; 硬件平台&#xff1a;x86_64&#xff08;amd64&#xff09;操作系统&#xff1a;CentOS 7.8 2003数据库版本&#xff08;含CSDK&#xff09;&#xff1a;GBase 8s V8.8 3.0.0_1 为什么使用QT 5.12.10&#xff1f;该版本包含QODBC。 1&#…

探索AIGC人工智能(Midjourney篇)(二)

文章目录 利用Midjourney进行LOGO设计 用ChatGPT和Midjourney的AI绘画&#xff0c;制作儿童绘本故事 探索Midjourney换脸艺术 添加InsightFaceSwap机器人 Midjourney打造专属动漫头像 ChatGPT Midjourney画一幅水墨画 Midjourney包装设计之美 Midjourney24节气海报插画…