细说 Angular 的依赖性注入

作者: 接灰的电子产品 | 来源:发表于2017-05-06 13:25 被阅读575次

什么是依赖性注入?

依赖性注入( Dependency Injection )其实不是 Angular 独有的概念,这是一个已经存在很长时间的设计模式,也可以叫做控制反转 ( Inverse of Control )。我们从下面这个简单的代码片段入手来看看什么是依赖性注入以及为什么要使用依赖性注入。

class Person {
  constructor() {
    this.address = new Address('北京', '北京', '朝阳区', 'xx街xx号');
    this.id = Id.getInstance(ID_TYPES.IDCARD);
  }
}

上面的代码中,我们在 Person 这个类的构造函数中初始化了我们构建 Person 所需要的依赖类: AddressId ,其中 Address 是个人的地址对象,而 Id 是个人身份对象。这段代码的问题在于除了引入了内部所需的依赖之外, 它知道了这些依赖创建的细节 ,比如它知道 Address 的构造函数需要的参数(省、市、区和街道地址)和这些参数的顺序,它还知道 Id 的工厂方法和其参数(取得身份证类型的 Id )。

但这样做的问题究竟是什么呢?首先这样的代码是非常难以进行单元测试的,因为在测试的时候我们往往需要构造一些不同的测试场景(比如我们想传入护照类型的 Id ),但这种写法导致你没办法改变其行为。其次,我们在代码的可维护性和扩展性方面有了很大的障碍,设想一下如果我们改变了 Address 的构造函数或 Id 的工厂方法的话,我们不得不去更改 Person 类。一个类还好,但如果几十个类都依赖 AddressPerson 的话,这会造成多大的麻烦?

那么解决的方法呢?也很简单,那就是我们把 Person 的构造改造一下:

class Person {
  constructor(address, id) {
    this.address = address;
    this.id = id;
  }
}

我们在构造中接受已经创建的 AddressId 对象,这样在这段代码中就没有任何关于它们的具体实现了。换句话说,我们把创建这些依赖性的职责向上一级传递了出去(噗~~推卸责任啊)。现在我们在生产代码中可以这样构造 Person

const person = new Person(
  new Address('北京', '北京', '朝阳区', 'xx街xx号'),
  Id.getInstance(ID_TYPES.IDCARD)
);

而在测试时,可以方便的构造各种场景,比如我们将地区改为辽宁:

const person = new Person(
  new Address('辽宁', '沈阳', '和平区', 'xx街xx号'),
  Id.getInstance(ID_TYPES.PASSPORT)
);

其实这就是依赖性注入了,这个概念是不是很简单?但有的同学问了,那上一级要是单元测试不还是有问题吗?是的,如果上一级需要测试,就得『推卸责任』到再上一级了。这样一级一级的最后会推到最终的入口函数,但这也不是办法啊,而且靠人工维护也很容易出错,这时候就需要有一个依赖性注入的框架来解决了,这种框架一般叫做 DI 框架或者 IoC 框架。这种框架对于熟悉 Java 和 .Net 的同学不会陌生,鼎鼎大名的 Spring 最初就是一个这样的框架,当然现在功能丰富多了,远不止这个功能了。

Angular 中的依赖性注入框架

Angular 中的依赖性注入框架主要包含下面几个角色:

  • Injector(注入者):使用 Injector 提供的 API 创建依赖的实例
  • Provider(提供者):Provider 告诉 Injector 怎样 创建实例(比如我们上面提到的是通过某个构造函数还是工厂类创建等等)。Provider 接受一个令牌,然后把令牌映射到一个用于构建目标对象的工厂函数。
  • Dependency(依赖):依赖是一种 类型 ,这个类型就是我们要创建的对象的类型。
Angular从零到一

相关文章

网友评论

  • 四爷在此:必须顶一下,父组件中Provider 实例化一个实例,子组件就可访问同一个实例而实现数据同步。很方便,感觉类型注入可以作为一种设计模式,模块间解耦的思想很有意思。。谢谢王老师通俗易懂的解析
    接灰的电子产品:@四爷在此 谢四爷点赞哦

本文标题:细说 Angular 的依赖性注入

本文链接:https://www.haomeiwen.com/subject/nswetxtx.html