1.介绍
依赖注入(Dependency Injection, DI)是一种软件设计模式,用于实现类与其依赖项之间的解耦。它的核心思想是,将类所依赖的对象通过外部注入的方式传递给它,而不是在类内部自行创建依赖对象。通过这种方式,代码变得更加灵活、可维护,并且有助于提高可测试性。
在面向对象编程中,类通常依赖于其他类来完成工作。例如,类 A 可能需要调用类 B 来执行一些功能。如果类 A 在内部直接创建类 B 的实例,那么这两者之间便紧密耦合,难以在不修改类 A 的情况下替换或测试类 B。
依赖注入 通过将依赖项(如类 B)从外部传递给类 A,可以解除两者之间的直接依赖。类 A 不再负责创建依赖项,而是依赖于外部提供的依赖项。
2.常见方式
2.1 构造函数注入具体类
// 具体的类
class Service {
public:
void execute() {
std::cout << "Executing service..." << std::endl;
}
};
class Client {
private:
Service* service;
public:
// 通过构造函数注入依赖项
Client(Service* s) : service(s) {}
void doWork() {
service->execute();
}
};
int main() {
Service s;
Client c(&s); // 注入依赖
c.doWork(); // 使用注入的依赖
return 0;
}
2.2 构造函数注入抽象类
// 抽象类,接口。
class IService {
public:
virtual void execute() = 0;
};
class Service : public IService {
public:
void execute() override {
std::cout << "Executing service..." << std::endl;
}
};
class Client {
private:
IService* service; // 抽象类,
public:
void setService(IService* s) {
service = s;
}
void doWork() {
service->execute();
}
};
int main() {
Service s;
Client c;
c.setService(&s); // 注入实现接口的依赖
c.doWork();
return 0;
}
2.3 set函数注入
class Client {
private:
Service* service;
public:
void setService(Service* s) {
service = s;
}
void doWork() {
service->execute();
}
};
int main() {
Service s;
Client c;
c.setService(&s); // 通过 setter 方法注入依赖
c.doWork();
return 0;
}
允许依赖在对象创建之后设置。
3.优点
-
解耦:通过依赖注入,类不再负责创建依赖项,而是依赖外部传递的对象,这大大降低了类之间的耦合性。松耦合,loose coupling。
-
可测试性:在测试时,可以方便地替换依赖项(如使用 mock 对象),从而更容易进行单元测试。
https://juejin.cn/post/7321967859242958857
依赖注入其实是控制反转(IOC)的一种实现方式。
需要控制反转的具体理由主要包括如下几点:
- 实际只需要对象提供的服务,不需要关心对象从何而来。
- 时空转变时,可能需要不同的对象来提供类似的服务,比如对于数据库操作服务,单元测试时需要模拟操作,而实际运行时需要真实操作。
- 拥有对象的控制权时,需要不断的改造自己,麻烦且容易出问题。比如依赖项的构造函数变化、具体实现的更改等。
- 将对象创建的控制权交出去,让外部场景来提供合适的服务对象。这可以让程序更容易组件化,更方便组合。