依赖注入模式
依赖注入(Dependency Injection,DI)指的是将应用程序所需的依赖关系或拓展组件通过构造函数参数或属性自动注入。
依赖倒置
依赖倒置(Dependence Inversion Principle,DIP)是 SOLID 里的 D。指高层模块,应当依赖于抽象类或者接口,而不是具体类和具体实现。
控制反转
控制反转(Inversion of Control,IoC),即反转调用方和被调用方的关系,实现解耦。
比如 A 类依赖 B 类,A 在内部实例化 B,此时就会出现耦合。如果在外部实例化 B,再注入到 A 内,就能实现解耦。
依赖注入
依赖注入(Dependency Injection,DI)是实现控制反转的过程。即在外部实例化 B,再注入到 A 内。
依赖注入的过程
typescript
class Tire {
run() {
console.log("我是轮胎,我得必须的,我得跑");
}
}
class AP {
run() {
console.log("我是智能驾驶,我得加钱");
}
}
class Car {
tire: Tire = new Tire();
ap: AP = new AP();
run() {
this.tire.run();
this.ap.run();
}
}
const myCar = new Car();
myCar.run();
以上代码中,轮胎**Tire**
和智能驾驶**AP**
作为车**Car**
的依赖项,被编入Car
的逻辑中,使得Car
类非常不灵活,如果用户提出想更改轮胎或不想要智能驾驶就需要去频繁修改Car
。所以,需要将Tire
和AP
抽离出来,将依赖关系从内部转移到外部:
typescript
class Tire {
run() {
console.log("一个普通轮胎");
}
}
class TireSpecial extends Tire {
run() {
super.run();
console.log("一个特殊的轮胎,得加钱");
}
}
class AP {
run() {
console.log("智能驾驶,得加钱");
}
}
class Car {
tire: Tire | TireSpecial;
ap: AP | null;
constructor(tire: Tire | TireSpecial = new Tire(), ap: AP | null = null) {
this.tire = tire;
this.ap = ap;
}
run() {
this.tire.run();
this.ap?.run();
}
}
const myBlankCar = new Car();
myBlankCar.run();
const mySpecialCar = new Car(new TireSpecial(), new AP());
mySpecialCar.run();
以上代码中,将Tire
和AP
作为构造函数参数传入,可以灵活的配置自己的Car
了,毛坯车 or 满配车!
自动注入
这种模式下,类的依赖可能会越来越复杂,所以可以通过外部 IOC 容器来创建和管理被依赖的类,实现自动注入。 站在巨人的肩膀上,让我们请出InversifyJS!
InversifyJS
一个轻量级 IOC 容器插件。
typescript
import { Container, inject, injectable } from "inversify";
import "reflect-metadata";
// 定义抽象类
interface TireAbstract {
run(): void;
}
interface APAbstract {
open(): void;
}
interface CarAbstract {
run(): void;
}
const TYPES = {
TireAbstract: Symbol.for("TireAbstract"),
APAbstract: Symbol.for("APAbstract"),
CarAbstract: Symbol.for("CarAbstract"),
};
// 基于抽象类的类实现
@injectable()
class Tire implements TireAbstract {
public run() {
console.log("一个普通轮胎");
}
}
@injectable()
class AP implements APAbstract {
public open() {
console.log("智能驾驶开启");
}
}
@injectable()
class Car implements CarAbstract {
private _tire: TireAbstract;
private _ap: APAbstract | null;
public constructor(@inject(TYPES.TireAbstract) tire: TireAbstract, @inject(TYPES.APAbstract) ap: APAbstract | null = null) {
this._tire = tire;
this._ap = ap;
}
run() {
this._ap?.open();
this._tire?.run();
}
}
// 创建容器,绑定所需要注入的依赖
const myContainer = new Container();
myContainer.bind<TireAbstract>(TYPES.TireAbstract).to(Tire);
myContainer.bind<APAbstract>(TYPES.APAbstract).to(AP);
myContainer.bind<CarAbstract>(TYPES.CarAbstract).to(Car);
const car = myContainer.get<CarAbstract>(TYPES.CarAbstract);
car.run();