前言
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運行後