记录生活中的点滴,分享、学习、创新
一、什么是依赖注入
控制反转(IoC)
控制反转的概念最早在2004年由Martin Fowler提出,是针对面向对象设计不断复杂化而提出的一种设计原则,是利用面向对象编程法则来降低应用耦合的设计模式。
IoC强调的是对代码引用的控制权由调用方转移到了外部容器,在运行是通过某种方式注入进来,实现了控制反转,这大大降低了程序之间的耦合度。依赖注入是最常用的一种实现IoC的方式,另一种是依赖查找。
依赖注入(Dependency Injection)
当然,按照惯例我们应该举个例子, 哦对,我们主要说明的是依赖注入,依赖查找请自行查阅资料。
假设我们有一个能做汉堡的设备(HRobot),需要用肉(meat)和一些沙拉(salad)作为原料,我们可以这样实现:
1 2 3 4 5 6 7 8 9 | export class HRobot { public meat: Meat; public salad: Salad; constructor() { this .meat = new Meat(); this .salad = new Salad(); } cook() {} } |
看一下好像没有什么问题,可能你已经发现,我们的原材料都是放在机器里面的,如果我们想吃别的口味的汉堡恐怕就要去乡村基了。
为了可以吃到别的口味的汉堡,我们不得不改造一下我们的HRobot:
1 2 3 4 5 6 7 8 9 | export class HRobot { public meat: Meat; public salad: Salad; constructor(public meat: Meat, public salad: Salad) { this .meat = meat; this .salad = salad; } cook() {} } |
现在,只要要直接给它meat和salad就好了,我们的HRobot()并不需要知道给它的是什么样的meat:
1 | let hRobot = new HRobot( new Meat(), new Salad()); |
比如,我们想吃鸡肉汉堡,只需要个它一块鸡肉就好:
1 2 3 4 5 | class Chicken extends Meat { meat = 'chiken' ; } let cRobot = new HRobot( new Chicken(), new Salad()); |
感觉还不错,我们再也不会为了吃一个鸡肉汉堡大费周章的去改造一台机器,这太不可思议了。
我可能想到了,你还是懒得弄块鸡肉给它,这时候可以使用工厂函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 | export class HRobotFactory { createHRobot() { let robot = new HRobot( this .createMeat(), this .createSalad()); } createMeat() { return new Meat(); } creatSalad() { return new Salad(); } } |
现在有了工厂,就有源源不断的汉堡可以吃了,开不开心,惊不惊喜?
好吧,没有最懒,只有更懒,连工厂都懒得管理我也是无话可说,幸运的是我们有Angular提供的依赖注入框架,它可以让你伸手就有汉堡吃!
二、 Angular依赖注入
在介绍Angular依赖注入之前,先来理一下三个概念:
说了半天到底是什么样的?
用代码示例如下:
1 2 3 | var injector = new Injector(...); var robot = injector.get(HRobot); robot.cook(); |
Injector()的实现如下:
1 2 3 4 5 6 7 | import { ReflecttiveInjector } form '@angular/core' ; var injector = ReflectiveInjector.resolveAndCreat([ {provide: HRobot, useClass: HRobot}, {provide: Meat, useClass: Meat}, {provide: Salad, useClass: Salad} ]); |
还有注入器是这样知道知道初始化HRobot需要依赖Meat和Salad:
1 2 3 4 5 | export class Robot { //... consructor(public meat: Meat, public salad: Salad) {} //... } |
当然,看了头大是应该的,因为上面的东西压根就不需要自己动手写,Angular的依赖注入框架已经自动帮我们完成了(注入器的生成和调用)。
1. 在组件中注入服务
Angular在底层做了大量的初始化工作,这极大地降低了我们使用依赖注入的成本,现在要完成依赖注入,我们只需要三步:
例子来了:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | // app.component.ts //... // 1. 导入被依赖对象的服务 import { MyService } from './my-service/my-service.service' ; @Component({ //... // 2. 在组件中配置注入器 providers: [ MyService ] //... }) export class AppComponent { // 3. 在构造函数中声明需要注入的依赖 constructor(private myService: MyService) {} } |
2. 在服务中注入服务
除了组件依赖服务,服务间依的相互调用也很寒常见。例如我们想给我们的汉堡机器人加上一个计数器,来记录它的生产状况,但是计数器又依靠电源来工作,我们就可以用一个服务来实现:
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 | // power.service.ts import { Injectable } from '@angular/core' ; @Injectable() export class PowerService { // power come from here.. } // count.service.ts import { Injectable } from '@angular/core' ; import { PowerService } from './power/power.service' ; @Injectable() export class CountService { constructor(private power: PoowerService) {} } // app.component.ts 这里是当前组件,其实模块中的注入也一样,后面讲到 //... providers: [ CountService, PowerService ] |
这里需要注意的是@Injectable装饰器是非必须的,因为只有一个服务依赖其他服务的时候才必须需要使用@Injectable显式装饰,来表示这个服务需要依赖,所以我们的PowerService并不是必须加上@Injectable装饰器的,可是,Angular官方推荐是否依赖其他服务,都应该使用@Injectable来装饰服务。
3. 在模块中注入服务
在模块中注册服务和在组件中注册服务的方法是一样的,只是在模块中注入的服务在整个组件中都是可用的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | // app.module.ts import { BrowserModule } from '@angular/platform-browser' ; import { NgModule } from '@angular/core' ;
*
|