化工企业网站模板,成都规划网站,搭建源码下载站网站,具有口碑的柳州网站建设公司C笔记之设计模式#xff1a;setter函数、依赖注入
参考笔记#xff1a; 1.C笔记之静态成员函数可以在类外部访问私有构造函数吗#xff1f; 2.C笔记之设计模式#xff1a;setter函数、依赖注入 3.C笔记之两个类的实例之间传递参数——通过构造函数传递类对象的方法详细探究…C笔记之设计模式setter函数、依赖注入
参考笔记 1.C笔记之静态成员函数可以在类外部访问私有构造函数吗 2.C笔记之设计模式setter函数、依赖注入 3.C笔记之两个类的实例之间传递参数——通过构造函数传递类对象的方法详细探究 4.C笔记之智能指针和单例、依赖注入结合使用 5.C笔记之两个类的实例之间传递参数的各种方法 code review! 文章目录 C笔记之设计模式setter函数、依赖注入1.概念2.基本示例3.setter函数4.基本示例setter函数构成依赖注入5.概念——ChatGpt6.构造函数注入示例7.接口注入示例8. 构造函数注入的使用场景和用途9.接口注入的使用场景和用途10.使用一个简单的业务逻辑来演示构造函数注入和接口注入的使用场景。10.1.构造函数注入示例10.2.接口注入示例 11.附加知识SOLID设计原则12.附加知识依赖倒置原则13.一个图形绘制应用程序。我们将创建一个图形类Shape并通过依赖注入来注入不同的绘制器Drawer来绘制不同的图形。 1.概念
依赖注入Dependency InjectionDI是一种软件设计模式用于管理和解决组件之间的依赖关系。在传统的编程中一个对象通常需要在自身内部创建或获取其所依赖的其他对象这可能会导致紧密耦合的代码使得代码难以测试、难以维护和难以扩展。依赖注入的目标是通过从外部传递依赖项来解耦组件提高代码的可测试性、可维护性和灵活性。
依赖注入的主要思想是一个组件被称为受注入对象不应该负责创建或获取其依赖的对象。相反这些依赖应该由外部实例化并通过构造函数、方法参数、属性等方式注入到受注入对象中。这种方式可以在不修改受注入对象的情况下灵活地替换依赖项的具体实现以及在测试时传递模拟对象。
依赖注入可以分为以下几种形式 构造函数注入通过构造函数将依赖项传递给受注入对象。这是最常见的依赖注入方式。通过构造函数依赖关系在对象创建时就被传递并在整个对象生命周期中保持稳定。 方法参数注入通过方法的参数将依赖项传递给对象的方法。这在需要特定依赖项执行特定操作的情况下非常有用。 属性注入通过公开的属性将依赖项传递给对象。这种方式可能导致依赖关系的意外更改因此在使用时需要小心。 接口注入通过接口或抽象类定义依赖项然后将具体实现传递给对象。这种方式允许在运行时替换依赖项的具体实现实现多态性。
依赖注入的优势包括 解耦和灵活性减少了组件之间的紧耦合使得代码更加灵活、可维护和易于扩展。 可测试性可以轻松地传递模拟对象或桩对象以进行单元测试从而提高代码的测试覆盖率。 可读性依赖关系在代码中被明确地传递使代码更易于理解。
简单理解来说就是当依赖的某个对象是通过外部来注入而不是自己创建。
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 iostreamclass 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 iostreamclass 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是一组五个面向对象编程和设计的原则旨在帮助开发者创建更加可维护、灵活和可扩展的软件。这些原则是 单一职责原则Single Responsibility PrincipleSRP 每个类应该有且仅有一个引起变化的原因即一个类应该只负责一项职责。这有助于将类的职责分离使代码更加模块化提高可维护性和可测试性。 开放封闭原则Open/Closed PrincipleOCP 软件实体类、模块、函数等应该对扩展开放对修改关闭。这意味着在不修改现有代码的情况下可以通过添加新代码来扩展功能。这有助于减少影响已有功能的风险。 里氏替换原则Liskov Substitution PrincipleLSP 子类必须能够替换其基类而不影响程序的正确性。这意味着子类应该能够保持基类的行为并且不应该破坏基类的约定。 接口隔离原则Interface Segregation PrincipleISP 不应该强迫客户端使用接口的类依赖于它们不需要的接口。这个原则鼓励将大型接口拆分成更小、更具体的接口以便客户端只需实现它们所需的部分。 依赖倒置原则Dependency Inversion PrincipleDIP 高层模块不应该依赖于低层模块两者都应该依赖于抽象。抽象不应该依赖于细节细节应该依赖于抽象。这个原则强调通过依赖于抽象来实现解耦和灵活性。
这些原则共同构成了SOLID原则它们的目标是指导开发者编写更加灵活、可扩展和易于维护的代码。这些原则并不是僵硬的规则而是一种指导思想根据实际情况和项目需求进行适当的应用和权衡。
12.附加知识依赖倒置原则
依赖倒置原则Dependency Inversion PrincipleDIP是SOLID设计原则中的一部分提出了一种关于如何设计和组织软件架构的指导思想。DIP的核心思想是
高层模块不应该依赖于低层模块。两者都应该依赖于抽象。抽象不应该依赖于细节。细节应该依赖于抽象。
这个原则强调了以下几个关键概念 高层模块指的是较高层次的组件、模块或类它们通常是实现业务逻辑的部分。 低层模块指的是较低层次的组件、模块或类它们通常是实现底层细节的部分比如工具类、数据库访问等。 抽象指的是接口、抽象类或其他形式的抽象层用于定义高层和低层之间的通信接口。 细节指的是具体的实现细节通常包括具体类、方法等。
依赖倒置原则的目标是减少组件之间的紧耦合提高系统的灵活性、可维护性和可扩展性。通过将高层模块和低层模块都依赖于抽象可以实现以下几个好处 可替换性高层模块可以轻松地切换不同的低层实现而不需要修改高层代码。 可测试性通过依赖于抽象可以更容易地进行单元测试以及在测试中使用模拟对象或桩对象。 模块解耦高层模块和低层模块之间的关系由抽象定义减少了紧密的依赖关系。 灵活性系统更容易适应变化因为只需要修改抽象或新的低层实现而不需要修改高层模块。
在之前提供的示例中使用接口注入的方式就体现了依赖倒置原则。通过依赖于抽象的接口或者基类高层模块和低层模块之间的关系由抽象定义达到了解耦和灵活性的目标。
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;
}