C++那些事之依赖注入

C++那些事之依赖注入

最近星球里面有个小伙伴让更新一下依赖注入,于是写出了这篇文章,来从实际的例子讲解,本文会讲解一些原理与实现,完整的实现代码懒人版放在星球中,我们开始正文。

大纲:

  • 直接依赖

  • 接口依赖注入

  • 模版依赖注入

依赖注入是一种常用的设计模式,通常会采用接口/模版的方式提高代码的可测性与维护性,下面我们来举个例子。

假设有一个用户服务类UserService,需要通过HTTP客户端与远程服务器进行通信,在这种情况下我们可能会写出如下三种实现。

1.直接依赖

UserService内部直接依赖HttpClient,例如:

go 复制代码
class HttpClient {
 public:
  std::string Get(const std::string& url);
  void Post(const std::string& url, const std::string& data);
};

class UserService {
 private:
  HttpClient m_httpClient;
};

UserService内部通常会有一些方法去操作m_httpClient,这种方式简单粗暴,但是UserService与具体的HttpClient相耦合,导致难以替换依赖对象,也难以进行单元测试。

于是引出了后面两个方法。

2.接口依赖注入

我们的目标是期望测试的时候很方便的测试,而不需要创建一个具体的类,可以由用户mock出一个类去测试,那么我们可以想到接口,将HttpClient抽出来,得到:

go 复制代码
struct IHttpClient {
  virtual std::string Get(const std::string& url) = 0;
  virtual void Post(const std::string& url, const std::string& data) = 0;
  virtual ~IHttpClient() = default;
};
class UserService {
 private:
  std::unique_ptr<IHttpClient> m_httpClient;
};

那么在测试的时候便可以mock出一个client进行测试,例如:

go 复制代码
class MockHttpClient : public IHttpClient {
public:
  MOCK_METHOD(std::string, Get, (const std::string& url), (override));
  MOCK_METHOD(void, Post, (const std::string& url, const std::string& data), (override));
};

auto mockHttpClient = std::make_unique<MockHttpClient>();
UserService userService(std::move(mockHttpClient));

经过以上改造,我们可以做到:

  • 在 .cc 文件中隐藏实现细节

  • 针对抽象接口工作

  • 在对象构造期间注入依赖项

3.模版依赖注入

模板依赖注入通过C++模板技术,将依赖对象作为模板参数传递给UserService。在实例化模板时,指定具体的HTTP客户端实现。这种方法在编译时解决依赖问题,无需虚函数调用,提高了性能。同时,也使得UserService类可以灵活地与不同的HTTP客户端实现进行集成,并且易于进行单元测试。

go 复制代码
template <typename THttpClient>
class UserService {
 private:
  THttpClient m_httpClient;
};

在这种情况下比较好测试,例如:

go 复制代码
UserService<MockHttpClient> userService;

这种方式有如下好处:

  • 在模板实例化期间注入依赖项

  • 无需虚拟调用(注意 THttpClient 不是接口,因此可以在编译时解析调用)

以上便是本节要讲的所有内容,本节完整代码放于星球,欢迎订阅下载!

跟我一起实践写代码,戳这里呀~

往期推荐:

向量数据库milvus源码剖析之开篇

热度更新,手把手实现工业级线程池

相关推荐
彧azz3 小时前
图的存储结构详解:邻接矩阵的原理、实现与应用
开发语言·数据结构·学习·php
IT_Octopus3 小时前
IntelliJ 本地日志路径自定义:`-DLOG_PATH=./logs` 为什么总“不听话“
java·log4j·intellij-idea
hansang_IR3 小时前
【题解】P4460 [CQOI2018] 解锁屏幕
c++·算法
嵌入式学习菌3 小时前
Modbus‑RTU 数据类型分析
开发语言·单片机·bug
ChampaignWolf4 小时前
在 SAP S/4HANA 内部基于 ICF Handler 从零实现 ABAP MCP Server:架构、代码与踩坑实录
c++·架构·sap·abap·mcp
matlab代码4 小时前
基于matlab数字图像处理的指纹识别系统【源码68期】
开发语言·matlab
matlab代码4 小时前
基于matlab的水果识别系统(苹果香蕉菠萝梨桃子)【源码65期】
开发语言·matlab
wuyk5555 小时前
从零吃透 MQTT 通信|第 8 章 FreeRTOS 多任务架构下 MQTT 工程架构,任务拆分、队列解耦、临界区保护
c语言·开发语言·stm32·学习·架构
weixin199701080165 小时前
[特殊字符]《跨境二手ERP对接Back Market:标准化API + 7~10工作日技术合规审核实录》(附Python源码)
开发语言·python·pandas
尘客-追梦5 小时前
Day 01:插件化之前,先看清 ABI 这堵墙
开发语言·c++·qt