• 【C++设计模式】依赖倒转原则


    2023年8月30日,周三上午


    目录


    概述

    依赖倒转原则(Dependency Inversion Principle,DIP)是面向对象设计中的一个基本原则。

    含义

    高层模块不应该依赖低层模块,两者都应该依赖其抽象。

    也就是说:

    • 高层模块不应该直接依赖低层模块,两者之间应使用抽象来解耦。
    • 具体实现应该依赖抽象,而不应该依赖细节。
    • 抽象不应该依赖细节,细节应该依赖抽象。

    举个简单的例子

    • 高层模块:用户模块
    • 低层模块:数据库模块
    • 抽象:接口或抽象基类

    传统做法

    这违反了依赖倒转原则,因为高层用户模块直接依赖了低层数据库模块。

    用户模块 -> 直接依赖数据库模块
    

    使用依赖倒转原则

    • 定义一个数据库操作的接口或抽象基类
    • 数据库模块实现这个接口/基类
    • 用户模块只依赖接口/基类,通过接口/基类与数据库模块解耦
    1. 依赖 实现
    2. 用户模块 -----> 接口/抽象基类<-----数据库模块

    这样一来,用户模块与数据库模块的依赖关系就通过抽象进行了解耦。如果需要替换数据库,只需要修改数据库模块的实现,不影响用户模块。

    总之,依赖倒转原则通过抽象层解耦高低层模块的依赖关系,提高了模块的独立性、可扩展性和可维护性。

    代码说明

    这里用一个简单的代码例子来说明依赖倒转原则。

    1. 依赖 实现
    2. UserModule -----> 抽象类IDataBase<-----MysqlDatabase

    首先定义一个数据库操作的接口:

    1. // 抽象接口
    2. class IDatabase {
    3. public:
    4. virtual void Insert(const string& data) = 0;
    5. virtual void Select() = 0;
    6. };

    然后实现这个接口的具体数据库类:

    1. // 具体实现
    2. class MysqlDatabase : public IDatabase{
    3. public:
    4. void Insert(const string& data) override {
    5. // 具体插入逻辑
    6. }
    7. void Select() override {
    8. // 具体查询逻辑
    9. }
    10. };

    用户模块只依赖接口,不依赖具体实现:

    1. // 用户模块
    2. class UserModule {
    3. private:
    4. IDatabase* db;
    5. public:
    6. UserModule(IDatabase* db) : db(db) {}
    7. void Run() {
    8. // ...
    9. db->Insert("some data");
    10. db->Select();
    11. }
    12. };

    在主函数中:

    1. int main() {
    2. MysqlDatabase mysql;
    3. UserModule user(&mysql);
    4. user.Run();
    5. return 0;
    6. }

    在这个程序里:

    • 用户模块只依赖抽象接口IDatabase,不依赖具体的MysqlDatabase类。
    • MysqlDatabase实现了IDatabase接口。
    • 通过接口解耦了用户模块和数据库模块的依赖关系。

    如果需要替换数据库,只需要修改MysqlDatabase实现,而不影响用户模块。这就是依赖倒转原则的实现。

    再举一个具体的例子

    在windows平台上用这套

    1. 依赖 实现
    2. DrawingProgram -----> 抽象类IShape<-----RectangleOnWindows

    在Linux平台上用这套

    1. 依赖 实现
    2. DrawingProgram -----> 抽象类IShape<-----RectangleOnLinux
    1. #include
    2. class IShape {
    3. public:
    4. virtual void draw() = 0;
    5. };
    6. //在windows平台上画矩形
    7. class RectangleOnWindows : public IShape {
    8. public:
    9. void draw() override {
    10. std::cout << "在Windows上画矩形" << std::endl;
    11. std::cout << "先画左边和右边,再画上边和下边" << std::endl;
    12. }
    13. };
    14. //在Linux平台上画矩形
    15. class RectangleOnLinux : public IShape {
    16. public:
    17. void draw() override {
    18. std::cout << "在Linux上画矩形" << std::endl;
    19. std::cout << "先画上边和左边,再画下边和右边" << std::endl;
    20. }
    21. };
    22. class DrawingProgram {
    23. private:
    24. IShape* shape;
    25. public:
    26. DrawingProgram(IShape* shape) {
    27. this->shape = shape;
    28. }
    29. void run() {
    30. shape->draw();
    31. }
    32. };
    33. int main() {
    34. //在Windows平台上用这一套
    35. RectangleOnWindows ROW;
    36. DrawingProgram program(&ROW);
    37. program.run();
    38. //在Linux平台上用这一套
    39. // RectangleOnLinux ROL;
    40. // DrawingProgram program(&ROL);
    41. // program.run();
    42. return 0;
    43. }

    这这个程序中:

    • 绘图程序只依赖形状接口,不依赖具体形状类。
    • 形状类实现了形状接口。
    • 通过接口解耦了绘图程序和形状类的依赖关系。

    如果需要添加新的形状,只需要实现形状接口,不影响绘图程序。这就是一个完整的依赖倒转原则示例。

    以生活为例

    电脑中的主板就是最好的一个依赖倒转原则例子,

    在主板上有非常多的硬件接口,用来安装内存、硬盘、电源等等,

    这些硬件接口就相当于抽象类,

    正是因为有了接口,才能在一块主板上安装不同品牌、不同厂商生产的内存条、硬盘、电源等等。

    如果主板上没有这些硬件接口,而是直接让主板与某个品牌的内存条连接,

    那么当这个内存条坏了,你就只能买这个品牌的内存条,用其他品牌的没用,

    因为这个主板是针对这个品牌的内存条设计的,没办法做到抽象,也就只能用这个品牌的。

  • 相关阅读:
    visionpro学习课程-CogPMAlignTool大总结
    Springboot毕业设计毕设作品,校园疫情防控小程序系统 开题报告
    基于Spring更简单的读取和存储对象
    使用Mysqldump进行定时全备和增备脚本
    【重识云原生】第四章云网络4.8.3.1节——Open vSwitch简介
    TypeSprict -- 基础类型
    2023年网络安全市场五大增长热点
    k8s--基础--22.7--storageclass--类型--vSphere
    前端基础建设与架构29 实践打造网关:改造企业 BFF 方案
    java调用python的方法
  • 原文地址:https://blog.csdn.net/m0_61629312/article/details/132575791