• 使用Typescript实现依赖注入(DI)


    前言
    DI总是和ico相辅相成的,如果想对DI有更多的了解,可以移步我的另一篇文章 依赖注入(DI)和控制反转(IOC),再次我就不多做赘述了。

    前几天看见一道面试题,今天借这个话题想跟大家分享一下:

    为什么在实际开发中,我们总是用DI,而不是用工厂模式,工厂模式也能实现同样的效果

    emmmm,想了一下,DI相当于是一种把当前对象和它所依赖的对象强解耦了,注入对象并不需要我们操心,而是把它委托给第三方,这个第三方可以是一些库或者框架,也可以是我们自己实现的ioc容器。而工厂模式,它是可以把我们需要的对象放进去,然后产生出我们最终需要的实例,但是创建这部分过程实际上还是由我们来做了。

    Typescript依赖注入
    javascript的动态类型,有时候会给我们带来很多的麻烦,实际上如果js能够在编译期就识别类型,那么性能会大大提升,比如webassembly。

    typescript不一样,它是js的超集,它始终会先用tsc编译一遍,再转换为js运行,它始终是js,但是ts在编译期就检查类型,是可以让我们避免很多的错误的。如果想了解更多typescript请移步ts官网

    typescript被很多框架所采用,比如angular,并且以它实现了依赖注入,我们在angular中,将一个类注册进ioc容器,只需给它附加一个injectable装饰器即可,比如:

    @Injectable()
    class User{
    //...
    }
    1
    2
    3
    4
    在angular中,我们把用injectable装饰器修饰的类叫做service,我们可以在任何我们需要User类的时候,注入进来,比如:

    class Main{
    constructor(priavte user:User){

    }
    }
    1
    2
    3
    4
    5
    只需在构造函数的参数上写上对user的依赖,那么ioc容器就会帮助我们把user注入进来。

    实现一个精简的DI
    其实DI的具体实现并不是很复杂,现在我们来实现一个精简版的DI。

    核心思想:根据类所声明的依赖,判断该依赖是否处于ioc容器中,如果处于,将它注入,并返回该类的实例,如果不属于,抛出一个异常,通知必须将依赖进行注册。

    大致分为两部分:

    ​ 1.注册

    ​ 2.创建实例

    先看看如何使用,再说具体实现

    假如现在有A,B,C三个类

    @Injectable()
    class C{
    constructor(){}
    }

    @Injectable()
    class B{
    constructor(private c:C){

    }
    }

    @Injectable()
    class A{
    constructor(priavte b:B){}
    }

    //产生实例
    let a:A = classFactory(A);
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    在A中声明对B的依赖,在B中声明对C的依赖。

    每个类都用injectable装饰器进行装饰,实际上是把它们放进了ioc容器。

    通过classFactory返回A的实例,此时b已经被注入进来了,同时c也已经注入进b,classFactory完成注入的动作。

    下面看一下具体实现:

    目录树

    src
    |-- index.ts
    |-- ioc.ts
    1
    2
    3
    ioc.ts

    //ioc容器
    let classPool:Array<Function> = [];

    //注册该类进入容器
    export function Injectable(){
    return (_constructor:Function) => {
    let paramTypes:Array<Function> = Reflect.getMetadata('design:paramtypes',_constructor)
    //已注册
    if(classPool.indexOf(_constructor) != -1) return;
    for(let val of paramTypes){
    if(val === _constructor) throw new Error('不能依赖自己')
    else if(classPool.indexOf(val) == -1) throw new Error(`${val}没有被注册`)
    }
    //注册
    classPool.push(_constructor);
    }
    }

    //实例化工厂
    export function classFactory<T>(_constructor:{new(...args:Array<any>):T}):T{
    let paramTypes:Array<Function> = Reflect.getMetadata('design:paramtypes',_constructor)
    //参数实例化
    let paramInstance = paramTypes.map((val:Function) => {
    //依赖的类必须全部进行注册
    if(classPool.indexOf(val) == -1) throw new Error(`${val}没有被注册`)
    //参数还有依赖
    else if(val.length){
    return classFactory(val as any);
    }
    //没有依赖直接创建实例
    else{
    return new (val as any)();
    }
    })
    return new _constructor(...paramInstance);
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    index.ts

    @Injectable()
    class C{
    constructor(){}
    }

    @Injectable()
    class B{
    constructor(private c:C){

    }
    }

    @Injectable()
    class A{
    constructor(priavte b:B){}
    }

    //产生实例
    let a:A = classFactory(A);
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    为了验证DI的有效性,可以为C声明一个实例方法,比如

    @Injectable()
    class C{
    constructor(){}

    sayHello(){
    console.log("hello")
    }
    }

    @Injectable()
    class B{
    constructor(private c:C){

    }

    sayHello(){
    this.c.sayHello();
    }
    }

    @Injectable()
    class A{
    constructor(priavte b:B){
    b.sayHello();
    }
    }

    //产生实例
    let a:A = classFactory(A);
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    运行后

    hello
    1
    参考:https://zhuanlan.zhihu.com/p/22962797
    ---------------------
    作者:小辣抓
    来源:CSDN
    原文:https://blog.csdn.net/HaoDaWang/article/details/79776021
    版权声明:本文为博主原创文章,转载请附上博文链接!

  • 相关阅读:
    各种数据库查询表及表信息的SQL
    多维表头的DataGridView
    SQLite入门笔记
    配置WCF的心得
    JS键盘的键码
    ASP.NET的URL过滤
    利用反射查看类成员
    一个简单的MVC示例
    一个日志类 LogUtil
    一个IniHelper
  • 原文地址:https://www.cnblogs.com/jacksplwxy/p/10644086.html
Copyright © 2020-2023  润新知