VSCode中依赖注入的原理是什么

工具使用   发布日期:2023年06月11日   浏览次数:556

这篇文章主要介绍“VSCode中依赖注入的原理是什么”,在日常操作中,相信很多人在VSCode中依赖注入的原理是什么问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”VSCode中依赖注入的原理是什么”的疑惑有所帮助!接下来,请跟着小编一起来学习吧!

依赖注入做了什么

假设以下情况:

  • 服务模块 A,依赖服务 B;

  • 服务模块 B;

  • 功能模块 Feature,依赖服务 A 和 B;

按照普通的写法就是:

  1. class B {}
  2. class A {
  3. constructor() {
  4. // 在 A 的构造器中 new B
  5. this.b = new B();
  6. }
  7. }
  8. class Feature {
  9. constructor() {
  10. this.a = new A();
  11. this.b = new B();
  12. }
  13. }
  14. // 使用时
  15. const feature = new Feature();

代码简单明了,存在一些问题,比如:如果 A 和 Feature 依赖的 B 需要是同一个实例,以上的写法将会初始化两个 B 实例。【推荐学习:vscode教程、编程教学】

简单修改一下:

  1. class A {
  2. constructor(b: B) {
  3. this.b = b;
  4. }
  5. }
  6. class Feature {
  7. constructor(a, b) {
  8. this.a = a;
  9. this.b = b;
  10. }
  11. }
  12. // 使用时
  13. const b = new B();
  14. const a = new A(b);
  15. const feature = new Feature(a, b);

某个模块初始化时,先在外部将其所依赖的模块创建出来,通过参数的形式传入功能模块。这样的写法就是「依赖注入」。

现在这种写法的问题在于:手动传参的形式,必须人工保证 new 的顺序,即必须获得 a, b 实例才能执行 new Feature。

当依赖关系变得复杂时,创建一个功能模块之前很有可能需要无数个基础模块,这时候复杂度将会非常高。

想象一种模式:存在一个模块控制器,或者说「服务管理器」来管理这些依赖关系:

  1. class Feature {
  2. // 声明这个模块依赖 idA, idB
  3. idA
  4. idB
  5. }
  6. // 告知「服务管理器」,怎么找对应的模块
  7. services[idA] = A;
  8. services[idB] = B;
  9. // 使用时
  10. const feature = services.createInstance(Feature);

这个 services 承载的不就是之前的「手工」过程吗?
在 createInstance(Feature) 时,分析 Feature 所依赖的模块:

  • 如果所依赖的模块还未创建出实例,递归创建出该服务实例,最终返回;

  • 如果所依赖的模块已有实例,返回该实例;

  • 找齐后通过参数注入 Feature,完成初始化;
    VSCode 实现的正是这么一套「依赖注入体系」。

依赖注入怎么做?

要实现这样一套功能,大致需要:

  • 一个类如何声明其依赖的服务 id,即给定一个 类,外部如何知道他依赖了哪些服务?

  • 如何管理管理服务?

  • 如何创建某个模块?

下文会实现一个最简单的模型,涵盖主体流程。

添加依赖信息

如何给一个 类 打上烙印,声明它所依赖的服务呢?
将问题再次抽象:如何给一个类加上额外的信息?
其实,每个类在 es5 下都是 Function,而每个 Function 说到底也只是 Object ,只要给 Object 加上几个字段来标识所需要的服务 id,就可以完成所需要的功能。
通过 「参数装饰器」的写法,可以很容易做到这一点:

  1. // 参数装饰器
  2. const decorator = (
  3. target: Object, // 被装饰的目标,这里为 Feature
  4. propertyName: string,
  5. index: number // 参数的位置索引
  6. ) => {
  7. target['deps'] = [{ index, id: 'idA', }];
  8. }
  9. class Feature {
  10. name = 'feature';
  11. a: any;
  12. constructor(
  13. // 参数装饰器
  14. @decorator a: any,
  15. ) {
  16. this.a = a;
  17. }
  18. }
  19. console.log('Feature.deps', Feature['deps']);
  20. // [{ id: 'idA', index: 0 }]

通过这种方式,通过 Feature (之后会称之为 构造器 ctor)就可以获取到 serviceId。

服务管理

使用 Map 来进行管理,一个 id 对应一个 服务 ctor。

  1. class A {
  2. name = 'a';
  3. }
  4. // 服务集
  5. class ServiceCollection {
  6. // 服务集合
  7. // key 为服务标识
  8. // value 为 服务ctor
  9. private entries = new Map<string, any>();
  10. set(id: string, ctor: any) {
  11. this.entries.set(id, ctor);
  12. }
  13. get(id: string): any {
  14. return this.entries.get(id);
  15. }
  16. }
  17. const services = new ServiceCollection();
  18. // 声明服务 A id 为 idA
  19. services.set('idA', A);

现在,就可以通过 Feature 来找到所依赖的服务的构造器了

  1. // 通过 Feature 找到所依赖的 A
  2. const serviceId = Feature['deps'][0].id; // idA
  3. console.log(
  4. 'Feature.deps',
  5. services.get(serviceId) // A
  6. );

模块创建

具体思路为:

  • 如果所依赖的模块还未创建出实例,递归创建出该服务实例,最终返回;

  • 如果所依赖的模块已有实例,返回该实例;

  • 找齐后通过参数注入 Feature,完成初始化;

这里先上一个简单的 demo,只有一层的依赖(即所依赖的服务没有依赖其他服务),简单的讲,就是没有递归能力:

  1. class InstantiationService {
  2. services: ServiceCollection;
  3. constructor(services: ServiceCollection) {
  4. this.services = services;
  5. }
  6. createInstance(ctor: any) {
  7. // 1. 获取 ctor 依赖的 服务id
  8. // 结果为: ['idA']
  9. const depIds = ctor['deps'].map((item: any) => item.id);
  10. // 2. 获取服务 id 对应的 服务构造器
  11. // 结果为:[A]
  12. const depCtors = depIds.map((id: string) => services.get(id));
  13. // 3. 获取服务实例
  14. // 结果为: [ A { name: 'a'} ]
  15. const args = depCtors.map((ctor: any) => new ctor());
  16. // 4. 依赖的服务作为参数注入,实例化所需要模块
  17. // 结果为:[ Feature { name: 'feature', a }]
  18. const result = new ctor(...args);
  19. return result;
  20. }
  21. }
  22. const instantiation = new InstantiationService(services);
  23. // 使用时
  24. const feature = instantiation.createInstance(Feature);

要使用 Feature 时,只需要调用 createInstance,不用管他所依赖的服务是否被初始化,instantiation 帮我们做了这个事情。

以上就是VSCode中依赖注入的原理是什么的详细内容,更多关于VSCode中依赖注入的原理是什么的资料请关注九品源码其它相关文章!