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