依賴注入框架 InversifyJS
背景
面向物件有五大原則:單一職責、開閉原則、里氏替換、介面分離和依賴反轉。依賴反轉(Dependency Inversion),實體應該依賴於抽象而不是實現。也就是說高層次模組,不應該依賴於低層次模組,而是應該基於抽象。
WebIDE 是函式計算團隊研發的一款產品,為了解決函式計算本地環境差異和配置繁瑣的問題。WebIDE 前端是 monorepo 風格的專案,即外掛化構建 WebIDE 前端。外掛之間存在依賴關係。構建、擴充套件和以及使用一個外掛將是一個複雜的問題,而且對使用外掛的開發人員不透明。通過使用 inversify 就能很簡單的實現。通過 inversify 能很容的實現外掛的構建、擴充套件和使用。
- 建立。將服務類注入到容器中
- 替換。通過 rebind api 可以在其他模組中從新繫結某個服務
- 使用。在類中通過裝飾器注入需要使用的服務,服務的具體實現不需要關心,容器為我們管理
需要了解 WebIDE 詳情,請移步:WebIDE 使用手冊
介紹
InversifyJS 是一個輕量級的依賴注入框架,大小隻有 4KB,可以用於 Javascript 應用中。
安裝
由於 InversifyJS 用到了反射來獲取裝飾器的相關元資料,所以需要額外安裝庫reflect-metadata
npm install inversify reflect-metadata --save
另外,InversifyJS 要求 Typescript >= 2.0 並且需要配置如下編譯引數:
{ "compilerOptions": { "target": "es5", "lib": ["es6", "dom"], "types": ["reflect-metadata"], "module": "commonjs", "moduleResolution": "node", "experimentalDecorators": true, "emitDecoratorMetadata": true } }
使用
步驟 1:定義介面
// file interfaces.ts // 定義服務物件標識 export const Warrior = Symbol.for('Warrior'); export const Weapon = Symbol.for('Weapon'); export const ThrowableWeapon = Symbol.for('ThrowableWeapon'); export interface Warrior { fight(): string; sneak(): string; } export interface Weapon { hit(): string; } export interface ThrowableWeapon { throw(): string; }
步驟 2:定義依賴
// file entities.ts import { injectable, inject } from 'inversify'; import 'reflect-metadata'; import { Weapon, ThrowableWeapon, Warrior } from './interfaces'; @injectable() export class Katana implements Weapon { public hit() { return "cut!"; } } @injectable() export class Shuriken implements ThrowableWeapon { public throw() { return "hit!"; } } @injectable() export class Ninja implements Warrior { public constructor( @inject(Weapon) protected katana: Weapon, @inject(ThrowableWeapon) protected shuriken: ThrowableWeapon ) {} public fight() { return this.katana.hit(); } public sneak() { return this.shuriken.throw(); } }
步驟 3:建立並配置 IOC/">IOC 容器
// file inversify.config.ts import { Container } from "inversify"; import { Warrior, Weapon, ThrowableWeapon } from "./interfaces"; import { Ninja, Katana, Shuriken } from "./entities"; const myContainer = new Container(); myContainer.bind<Warrior>(Warrior).to(Ninja); myContainer.bind<Weapon>(Weapon).to(Katana); myContainer.bind<ThrowableWeapon>ThrowableWeapon).to(Shuriken); export { myContainer };
步驟4:依賴解析
import { myContainer } from "./inversify.config"; import { Warrior } from "./interfaces"; const ninja = myContainer.get<Warrior>(Warrior); expect(ninja.fight()).eql("cut!"); // true expect(ninja.sneak()).eql("hit!"); // true
小結
如果你熟悉 Spring,Spring 很多特性在 Inversify 中可以找到,如果你的專案規模比較大,可以採用 monorepo 多包結構來構建專案。每一個包(模組)包含一個 ContainerModule 容器管理本模組依賴,然後在專案入口對所有的模組容器進行統一載入。