十几行代码实现一个ts依赖注入

栏目: 后端 · 发布时间: 6年前

内容简介:IOC(Inversion of Control)即控制反转,DI(Dependency Injection)即依赖注入。假设我们有一个类当需求达到一定复杂的程度时,我们不能为了一个人穿衣服去从布从纽扣开始从头实现,最好能把所有的需求放到一个工厂或者是仓库,我们需要什么直接从工厂的仓库里面直接拿。

IOC(Inversion of Control)即控制反转,DI(Dependency Injection)即依赖注入。

假设我们有一个类 Humen ,要实例一个 Humen ,我们需要实例一个类 Clothes 。而实例化衣服 Clothes ,我们又需要实例化布 Cloth ,实例化纽扣等等。

当需求达到一定复杂的程度时,我们不能为了一个人穿衣服去从布从纽扣开始从头实现,最好能把所有的需求放到一个工厂或者是仓库,我们需要什么直接从工厂的仓库里面直接拿。

这个时候就需要依赖注入了,我们实现一个IOC容器(仓库),然后需要衣服就从仓库里面直接拿实例好的衣服给人作为属性穿上去。

IOC是一种很好的解耦合思想,在开发中,IoC意味着你设计好的对象交给容器控制,而不是使用传统的方式,在对象内部直接控制。在软件开发中有很好的作用,不仅被应用在JavaEE里,在其它语言里同样适用。

其实总结起来大概就是你需要我,工厂就把我打扮好派我去找你。

实现一个IOC容器

因为key类型不一定是字符型,而且数组遍历比较浪费性能,因此不选择Object和Array而选择Map作为容器的数据结构。

因为目标是实现一个可以懒汉模式的IOC容器,所以可以在类 Injector 里写了两个私有类,一个存token和依赖,一个存token和实例化的依赖实例。

在需要某个依赖实例的时候,先去实例的Map中根据token查找出对应的token的实例,如果没有就从存放依赖类的Map中拿出来实例化之后再放入存放实例的Map。

export class Injector {
  private readonly providerMap: Map<any, any> = new Map();
  private readonly instanceMap: Map<any, any> = new Map();
  public setProvider(key: any, value: any): void {
    if (!this.providerMap.has(key)) this.providerMap.set(key, value);
  }
  public getProvider(key: any): any {
    return this.providerMap.get(key);
  }
  public setInstance(key: any, value: any): void {
    if (!this.instanceMap.has(key)) this.instanceMap.set(key, value);
  }
  public getInstance(key: any): any {
    if (this.instanceMap.has(key)) return this.instanceMap.get(key);
    return null;
  }
  public setValue(key: any, value: any): void {
    if (!this.instanceMap.has(key)) this.instanceMap.set(key, value);
  }
}
复制代码

现在new一下,这个容器就创建出来了。

export const rootInjector = new Injector();
复制代码

实现服务

偷偷借走了ng的 Injectable ,通过类装饰器把类存入容器。

export function Injectable(): (_constructor: any) => any {
  return function (_constructor: any): any {
      rootInjector.setProvider(_constructor, _constructor);
      return _constructor;
  };
}
复制代码

把类作为token,把该类存入provider容器,提供给需要依赖的类。

实现基于注解的属性注入

之所以不实现构造注入,setter注入是因为像ng和react这类框架会对直接对构造函数进行注入或是限制构造函数的参数,为了尽量跨框架跨前后端使用,所以还是用装饰器对属性注入尽量减少侵入性。

属性装饰器和反射能帮我们实现这一功能。

export function Inject(): (_constructor: any, propertyName: string) => any {
  return function (_constructor: any, propertyName: string): any {
    const  propertyType: any = Reflect.getMetadata('design:type', _constructor, propertyName);
    const injector: Injector = rootInjector;

    let providerInsntance = injector.getInstance(propertyType);
    if (!providerInsntance) {
        injector.getProvider(propertyType);
        providerInsntance = new providerClass();
        injector.setInstance(key, providerInsntance);
    }
    _constructor[propertyName] = providerInsntance;

    return (_constructor as any)[propertyName];
  };
}
复制代码

使用Reflect的元数据 Reflect.getMetadata('design:type') 获取属性的类型,并作为token去 injector.getInstance 查询对应的实例,如果有则直接将属性映射为查找到的实例。这样就保证我们每次使用装饰器的属性都会获得单例。

如果没有查询到则去另外一个Map中拿出依赖并实例化存入实例的Map。因为js单线程懒汉模式不存在线程安全这么一说,所以没有选择初始化时就把全部的依赖实例化。

demo

import { Inject, Injectable } from '../injector';

@Injectable()
class Cloth {
    public name: string = '麻布';
}

@Injectable()
class Clothes {
  @Inject() public cloth: Cloth;
}

class Humen {
  @Inject() public clothes: Clothes;
}

const pepe = new Humen();
console.log(pepe);
// {
//   clothes: {
//      cloth: {
//        name: '麻布'
//      }
//   }
//}
复制代码

最后我们可以直接new pepe,不需要关心pepe穿的衣服 Clothes 和衣服需要的布料 Cloth

最后推荐结合 rxjs 做响应式编程。 ng大法好!


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

The Cult of the Amateur

The Cult of the Amateur

Andrew Keen / Crown Business / 2007-6-5 / USD 22.95

Amateur hour has arrived, and the audience is running the show In a hard-hitting and provocative polemic, Silicon Valley insider and pundit Andrew Keen exposes the grave consequences of today’s......一起来看看 《The Cult of the Amateur》 这本书的介绍吧!

在线进制转换器
在线进制转换器

各进制数互转换器

正则表达式在线测试
正则表达式在线测试

正则表达式在线测试

HSV CMYK 转换工具
HSV CMYK 转换工具

HSV CMYK互换工具