• 【设计模式】如何用C++实现依赖倒置


    【设计模式】如何用C++实现依赖倒置

    一、什么是依赖倒置?

    依赖倒置原则(Dependency Inversion Principle,DIP)是SOLID面向对象设计原则中的一项。它的核心思想是:

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

    这个原则的目的在于减少代码耦合性,使代码更具灵活性和扩展性。按照依赖倒置原则,我们让上层的逻辑不直接依赖底层实现,而是通过抽象(接口或抽象类)来控制两者之间的关系。

    二、为什么使用依赖倒置?

    在没有依赖倒置的设计中,高层模块(如业务逻辑)往往会直接依赖于低层模块(如数据访问、服务调用)。这种依赖会使得高层模块无法独立更改,一旦低层模块发生变动,所有依赖它的高层模块都需要跟着修改,导致代码维护难度和出错率增加。

    通过依赖倒置可以实现以下特性:

    • 更易于扩展:可以随时替换低层模块的实现而不需要改动高层模块。
    • 增强测试性:可以方便地替换低层模块,便于Mock或打桩,提升了代码的可测试性。
    • 提高灵活性:可以根据需求切换不同的实现,方便扩展和维护。

    三、实现步骤

    1. 定义抽象接口(基类):定义一个抽象类Service.h实现了一个Test纯虚函数,此抽象类不实现任何具体逻辑,仅对外提供接口

      Service.h
      #ifndef DIP_SERVICE
      #define DIP_SERVICE
      
      namespace DIP
      {
          class Service
          {
          public:
              virtual ~Service() = default;
              virtual void Test() = 0;
          };
      }
      
      #endif
      
    2. 实现接口的具体类:定义一个实现类ServiceImpl实现构造、析构、Test等函数逻辑。

      ServiceImpl.h
      #ifndef DIP_SERVICEIMPL
      #define DIP_SERVICEIMPL
      #include "Service.h"
      
      namespace DIP
      {
          class ServiceImpl : public Service
          {
          public:
              ServiceImpl(int a);
              ~ServiceImpl() override;
              void Test() override;
          private:
              int m_a;
          };
      }
      
      #endif
      
      ServiceImpl.cpp
      #include "ServiceImpl.h"
      #include 
      
      using namespace DIP;
      
      ServiceImpl::ServiceImpl(int a) : m_a(a)
      {
          std::cout << "In ServiceImpl, m_a = " << m_a << "." << std::endl;
      }
      
      ServiceImpl::~ServiceImpl()
      {
          std::cout << "In ~ServiceImpl, m_a = " << m_a << "." << std::endl;
      }
      
      void ServiceImpl::Test()
      {
          m_a++;
          std::cout << "In Test, m_a = " << m_a << "." << std::endl;
      }
      
    3. 注入依赖:通过构造函数依赖注入,将实现DIP::Service接口的对象传入,将具体实现传递给高层模块的接口指针或引用,从而对具体实现DIP::ServiceImpl解耦。

      main.cpp
      #include "ServiceImpl.h"
      #include "Service.h"
      #include 
      
      int main()
      {
          int a = 3;
          std::shared_ptr<DIP::Service> serviceImpl = std::make_shared<DIP::ServiceImpl>(a);
          serviceImpl->Test();
      }
      

      在这种设计中,高层的main函数依赖于抽象接口Service,而不是具体的实现类。这样只要继承了Service接口的实现类都可以被使用。这就是依赖倒置带来的灵活性和扩展性。

    四、完整实现

    1. 在以上三点的基础上继续编写CMakeLists.txt文件。

      # 设置项目名称和最低CMake版本
      cmake_minimum_required(VERSION 3.10)
      set(ProjectName Service)
      project(${ProjectName})
      
      set(CMAKE_CXX_STANDARD 17)
      set(CMAKE_CXX_STANDARD_REQUIRED True)
      
      add_executable(${ProjectName} main.cpp ServiceImpl.cpp)
      
    2. 此时代码结构如下。

      在这里插入图片描述

    3. 命令行编译执行。

      mkdir build
      cd build
      cmake ..
      make -j12
      ./Service
      
    4. 执行结果。

      In ServiceImpl, m_a = 3.
      In Test, m_a = 4.
      In ~ServiceImpl, m_a = 4.
      
    5. 可以看到Service指针在不感知ServiceImpl具体实现的情况下,仅通过调用接口实现了和ServiceImpl实例相同的功能。

  • 相关阅读:
    【无标题】
    Python入门 | 是循环,也是遍历
    计算机毕业设计springboot+vue基本微信小程序的快递收发小程序
    网络-TCP关闭连接(close、shutdown)
    Tomcat 7.0 win7 64位免安装解压版 安装及配置
    【机器学习】朴素贝叶斯算法:多项式、高斯、伯努利,实例应用(心脏病预测)
    Photoshop学习笔记的小tips分享
    sql跨表查询的三种方案
    Print()函数用法实例详解
    puppeteer的简单使用
  • 原文地址:https://blog.csdn.net/zhangjiuding/article/details/143416394