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

前言
DI老是和ico相辅相成的,若是想对DI有更多的了解,能够移步个人另外一篇文章 依赖注入(DI)和控制反转(IOC),再次我就很少作赘述了。javascript

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

为何在实际开发中,咱们老是用DI,而不是用工厂模式,工厂模式也能实现一样的效果web

emmmm,想了一下,DI至关因而一种把当前对象和它所依赖的对象强解耦了,注入对象并不须要咱们操心,而是把它委托给第三方,这个第三方能够是一些库或者框架,也能够是咱们本身实现的ioc容器。而工厂模式,它是能够把咱们须要的对象放进去,而后产生出咱们最终须要的实例,可是建立这部分过程实际上仍是由咱们来作了。面试

Typescript依赖注入
javascript的动态类型,有时候会给咱们带来不少的麻烦,实际上若是js可以在编译期就识别类型,那么性能会大大提高,好比webassembly。typescript

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){this

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

实现一个精简的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);1234567891011121314151617181920212223242526272829运行后

相关文章
相关标签/搜索