前端需要了解的依赖注入
作为前端,会经常碰到依赖注入,对于其原理还不是很了解,作为一个学习者,在阅读了大量依赖注入相关文档后,以前端更能理解的方式来表达,所以本身对依赖注入的了解不是很深入,旨在帮助前端的同学理解。
依赖注入基本概念
依赖注入是指让一个对象 A 作为另一个对象 B 的依赖,使 B 能使用 A。
从上面这句话应该能明白三个问题:什么是依赖?注入到什么里边?
在注入关系中,依赖可以被注入到很多其他对象中,相当于「服务端」,而接受注入的那个对象能够被注入(接受)很多依赖,相当于「客户端」。有了服务端和客户端,还需要「注射器」将服务端的内容发送(注射)到客户端。需要注意的是,依赖注入模式要求将「服务端」注入「客户端」,即「客户端」是被注入的关系。
为什么需依赖注入
这得谈一下依赖注入和解耦的关系,可以参考这篇文章的内容,这篇文章理清了控制反转(IOC)、依赖注入(DI)和耦合的关系。系统的耦合之处在于各个组件之间相互依赖,有的时候会相互引用甚至要创建依赖的部分,解耦的一个方法就是不让这些耦合组件直接相互调用,而是在使用的时候从一个中心取用,而中心再去找到真正需要调用的模块,这样每个组件只需要和中心进行交流,从而避免了组件之间高度耦合。
在解耦之前,引用依赖的过程就类似于自助,而有了一个统一的中心之后类似于有人提供服务,对于组件来说权限更小了。
2004 年,Martin Fowler 探讨了同一个问题,既然 IOC 是控制反转,那么到底是“哪些方面的控制被反转了呢?”,经过详细地分析和论证后,他得出了答案:“获得依赖对象的过程被反转了”。控制被反转之后,获得依赖对象的过程由自身管理变为了由 IOC 容器主动注入。于是,他给“控制反转”取了一个更合适的名字叫做“依赖注入(Dependency Injection)”。他的这个答案,实际上给出了实现 IOC 的方法:注入。所谓依赖注入,就是由 IOC 容器在运行期间,动态地将某种依赖关系注入到对象之中。
所以控制反转是实现解耦的一种理念,而依赖注入则是控反转的具体实现。
依赖注入的几种方式
依赖注入的核心就是,所有依赖都不是自己的,而是从外边拿的。把依赖当作一个对象变量,要满足这个条件,可以用三种方式实现:
- 通过构造函数注入
- 通过对象方法来设置
假设依赖是这样:
1 | class Dep { |
每一个模块就可以这样写:
方式一
1 | class ModA { |
注意是传入一个已经实例化的依赖,而不是自己实例化依赖。
方式二
1 | class ModA { |
当改变dep
属性的时候a
中的dep
属性也会跟着变化,这样使用这些依赖就不用再考虑如何改变依赖,控制权并不在模块之中。
和上一种方式大同小异,只是绑定依赖的时刻不一样,本质上都是传递一个在外部实例化的对象。
依赖注入实现
上面代码提供了思路,但对于一个系统来说是并不完整的,上面的代码体现了几点问题:
- 每次都需要单独写一段代码来手动初始化依赖,虽然不是在模块内部初始化,但是不太简洁
- 第一个问题就导致了,有大量依赖的时候不便于管理
真正的依赖注入还需要一个服务中心来进行管理和依赖分发,就像之前所说的那样。
一个服务中心,至少需要实现两个功能:
- 服务注册,创建一个容器用于存放依赖
- 注入服务
服务注册这个地方可以使用一个对象来存储,注入服务可以让模块统一暴露设置接口,通过接口来设置依赖。
参考资料
相关阅读
前端需要了解的依赖注入