image.png

Dependencty Injection

依赖注入 DI 是一个

OOP变成是基于实例instance 和 类Class 的,如果要使用功能,需要先实例化才能调用功能

遵循单一职责,复杂代码不会都写在一起,会分离多个子模块。

我们希望不同模块之间耦合度不要太高,如果底层模块发生变化,整个程序的改动会非常多,可靠性下降。

  1. import B from ‘../def/B’;
  2. import createC from ‘../def/C’;
  3. class A{
  4. constructor(paramB, paramC){
  5. this.b = new B(paramB);
  6. this.c = createC(paramC);
  7. }
  8. actionA(){
  9. this.b.actionB();
  10. }
  11. }

代码风格不好,A除了需要承担其本身的职责之外,还额外承担了B和C的实例化任务

如果对actionA方法进行单元测试,理论上只要actionB方法执行正确,实际上已经变成了包含B实例化过程、C实例化过程以及actionB方法调用的小范围集成测试

依赖注入DI就是为了解决上述问题,这种编程模式中,在构造函数里接收已经实例化好了的对象

  1. class A{
  2. constructor(bInstance, cInstance){
  3. this.b = bInstance;
  4. this.c = cInstance;
  5. }
  6. actionA(){
  7. this.b.actionB();
  8. }
  9. }

这种时候,实例化过程在外部,A只需要专注自己的逻辑。

实例化在外部,这个外部称为容器,类似于 类注册表+工厂方法。用key-value 的注册表把各个类容器注册到容器里。

再让容器来控制类的实例化过程,如果需要用到实例的时候,容器会自动对依赖分析,生成需要的实例注入到构造函数中。当然会缓存

在这种模式下,强依赖消失了,不过传入的实例是啥,只要有对应的方法就行。鸭式辨形,说的就是这里了,ts里通过interface约束就完了。

DI实际上是 IOC的一种体现

DI:

  • 上层模块不应该依赖底层模块,应该依赖共同的抽象
  • 抽象不应该依赖细节,细节依赖抽象

从手动生成变成了 容器自动分析并注入,降低了耦合,隐藏了细节。也让清晰的代码晦涩,但对需求不确定有了抵御能力。

  1. // IOC成员属性 接口1
  2. interface iIOCMember {
  3. factory: Function;
  4. singleton: boolean;
  5. instance?: {}
  6. }
  7. // 定义IOC容器
  8. Class IOC {
  9. // 私有 map实例
  10. // PropertyKey类型,这是Typescript中预设的类型,
  11. // 指string | number | symbol的联合类型,也就我们平时用作键的类型
  12. private container: Map<PropertyKey, iIOCMember>;
  13. constructor() {
  14. this.container = new Map<string, iIOCMember>();
  15. }
  16. }

参考

https://juejin.cn/post/6925977528397987847