利用TS快速實現一個IOC容器
阿新 • • 發佈:2021-01-05
技術標籤:前端
實現一個簡單的IOC
前端應用在不斷壯大的過程中,內部模組間的依賴可能也會隨之越來越複雜,模組間的 低複用性 導致應用 難以維護,不過我們可以藉助計算機領域的一些優秀的程式設計理念來一定程度上解決這些問題,接下來要講述的
IoC
就是其中之一。
什麼是IOC
IoC
的全稱叫做 Inversion of Control
,可翻譯為為「控制反轉」或「依賴倒置」,它主要包含了三個準則:
- 高層次的模組不應該依賴於低層次的模組,它們都應該依賴於抽象
- 抽象不應該依賴於具體實現,具體實現應該依賴於抽象
- 面向介面程式設計 而不要面向實現程式設計
假設我們有一個類Human
,要例項一個Human
,我們需要例項一個類Clothes
。而例項化衣服Clothes
,我們又需要例項化布Cloth
,例項化鈕釦等等。
當需求達到一定複雜的程度時,我們不能為了一個人穿衣服去從布從鈕釦開始從頭實現,最好能把所有的需求放到一個工廠或者是倉庫,我們需要什麼直接從工廠的倉庫裡面直接拿。
這個時候就需要依賴注入了,我們實現一個IOC容器(倉庫),然後需要衣服就從倉庫裡面直接拿例項好的衣服給人作為屬性穿上去。
這也就大大減少了我們編碼的成本。
實現一個簡單的ioc
隨著裝飾器的出現,讓我們實現ioc成為了可能。現在在最新的es規範中已經實現該能力。
第一步:定義一個容器,用於存放需要ioc的類和物件
export class DIContainer<T> {
private container = new Map<string, T>();
private static instance: any;
public get(key: string) {
if (this.container.get(key)) {
return this.container.get(key);
}
return null;
}
public set(key: string, value: T) {
this.container.set(key, value);
}
public remove(key: string) {
if (this.container.get(key)) {
this.container.delete(key);
}
}
}
這裡暫時用一個簡單的map物件進行維護,方便我們快捷存取。
第二步:定義我們需要賦值的註解(這裡的話暫時我只實現了針對屬性賦值)
import { BaseDIInterface } from "..";
import { DIContainer } from "../container/DIContainer";
/**
* 自動注入service
* @param _key 預設用屬性名稱注入,也可以利用傳入自定義值注入
*/
export function AutoWried(_key?: string) {
return (target:any, attr:string) =>{
const diContainer = DIContainer.getContainer<BaseDIInterface>() as DIContainer<BaseDIInterface>;
target[attr] = diContainer.get(attr) ? diContainer.get(attr) : null;
return target;
}
}
第三步:我們需要在js執行開始之前將所有的物件都load進入容器進行維護
import { QuartzService } from './service/quartz/quartz_service';
import { BaseDIInterface, DIContainer } from './di';
loadAllContainer();
第四步:使用
@AutoWried()
private quartzService!: QuartzService;
經過上面所說的4步,我們就可以完成對一個依賴的注入和使用操作。
對了這裡使用了裝飾器,所以我們在ts的配置上需要允許裝飾器使用。
"experimentalDecorators": true,
提升和優化
在此處還是有可以提升的地方,比如說容器能力,我們可以更加完善,在初始化load專案的時候也可以通過裝飾器來實現load,而不是單純的像現在一個手工load。
後續我會完善該DI框架,併發布該專案。