依赖注入是通过外部传入依赖对象来解耦模块的设计方式,核心是控制反转,常用构造函数注入,配合抽象接口(如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。
实际降低耦合的关键细节
- 避免在类内部 new 具体实现类,所有 new 都应集中在少数“装配点”(如 main() 或工厂类)。
- 使用智能指针(std::unique_ptr / std::shared_ptr)管理生命周期,避免裸指针带来的所有权模糊。
- 依赖项尽量用 const 引用或 const 智能指针,表明只读使用,增强语义清晰度。
- 不要为了注入而注入——简单工具类(如 MathUtils)或无状态函数通常无需 DI。