依赖注入 (Dependency Injection)
这是一种重要的设计模式。
为什么要用依赖注入
下面例子中没有用依赖注入:
export class Car {
public engine: Engine;
public tires: Tires;
public description = 'No DI';
constructor() {
this.engine = new Engine();
this.tires = new Tires();
}
// Method using the engine and tires
drive() {
return `${this.description}car with ` +
`${this.engine.cylinders}cylinders and${this.tires.make}tires.`;
}
}
即 Car 构造函数内部创建和管理依赖的外部类,当外部类有变化时,Car 代码也要相应变化。
而使用依赖注入,即将创建 Car 对象时所需的外部依赖以构造器参数的形式传入。从而进行了依赖解耦,当外部依赖变化时,Car 类中的代码本身就不需要变化了:
public description = 'DI';
constructor(public engine: Engine, public tires: Tires) { }
当然,创建外部依赖类对象的代码还是要变化,这一般通过工厂类来实现:
import { Engine, Tires, Car } from './car';
// BAD pattern!
export class CarFactory {
createCar() {
let car = new Car(this.createEngine(), this.createTires());
car.description = 'Factory';
return car;
}
createEngine() {
return new Engine();
}
createTires() {
return new Tires();
}
}
工厂类的维护量会很大。
Angular 内置了一个依赖注入框架,相当于能自动维护这个工厂类。它相当于一个注入器,或喷嘴。当将 Car 等类登记到这个注入器中后,在需要类实例时只需要求注入器返回即可:
let car = injector.get(Car);
Angular 的依赖注入
Angular 在启动应用过程中会自动创建一个应用级的注入器:
//src/main.ts (bootstrap)
platformBrowserDynamic().bootstrapModule(AppModule);
在 NgModule 中登记提供者
在 AppModule 的 @NgModule 中登记的提供者具有应用级作用域。而在所有即时导入的模块中登记的提供者,在 AppModule 的 imports 列表中导入后,其提供者会自动加到应用级的所有提供者列表的后面。
//src/app/app.module.ts (excerpt)
@NgModule({
imports: [
BrowserModule
],
declarations: [
AppComponent,
CarComponent,
HeroesComponent,
/* . . . */
],
providers: [
UserService,
{ provide: APP_CONFIG, useValue: HERO_DI_CONFIG }
],
bootstrap: [ AppComponent ]
})
export class AppModule { }
在组件中登记提供者
每个组件实现都会有一个独立的注入器,因此这些提供者的作用域是该组件实现及其所有子组件。
依赖注入的使用方法
依赖注入模式中,组件必须通过在其构造器函数来要求服务注入,例如:
//src/app/heroes/hero-list.component (with DI)<