使用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
版權聲明:本文為博主原創文章,轉載請附上博文鏈接!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM