依赖注入是通过外部传入依赖对象来解耦模块的设计方式,核心是控制反转,常用构造函数注入,配合抽象接口(如DatabaseInterface)和智能指针实现松耦合、易测试。

依赖注入是一种让类不自己创建依赖对象,而是由外部把依赖“送进来”的设计方式。它不解决具体功能问题,而是调整代码结构,让模块之间关系更松散、更易替换、更易测试。
依赖注入的核心是“控制反转”
传统写法里,一个类自己 new 依赖对象,比如:
class UserService {
Database db;
public:
UserService() : db("localhost") {} // 自己决定用哪个数据库
};
这样 UserService 和 Database 强绑定,换数据库就得改代码。而用依赖注入后:
class UserService {
Database& db; // 或指针/智能指针
public:
explicit UserService(Database& d) : db(d) {} // 依赖由外面传入
};
// 使用时:
Database mysql("192.168.1.100");
UserService service(mysql);
谁创建 Database、用 MySQL 还是 SQLite,都和 UserService 无关了——控制权从类内部转移到了外部调用者。
立即学习“C++免费学习笔记(深入)”;
三种常见注入方式(C++ 中常用)
- 构造函数注入:最推荐。依赖在对象创建时一次性传入,状态明确、不可变,适合必需依赖。
- Setter 注入:通过 public 成员函数设置依赖,适合可选依赖或运行时可能变更的场景(如日志输出目标)。
-
接口注入(较少见):定义注入接口(如 Injectable
),被注入类实现该接口。C++ 中因缺乏反射支持,一般不实用,多用于框架抽象层。
配合抽象接口才能真正解耦
只传具体类型(如 Database)仍不够——换数据库还得改参数类型。关键一步是依赖抽象而非实现:
class DatabaseInterface {
public:
virtual ~DatabaseInterface() = default;
virtual void connect() = 0;
virtual void query(const std::string&) = 0;
};
class MySQLDB : public DatabaseInterface { / 实现 / };
class SQLiteDB : public DatabaseInterface { / 实现 / };
class UserService {
std::unique_ptr db;
public:
explicit UserService(std::unique_ptr d)
: db(std::move(d)) {}
};
此时 UserService 只知道“能连、能查”,完全不知道底层是 MySQL 还是 SQLite。单元测试时还能轻松注入 MockDatabase。











