IOC
(控制反转)是一种编程思想,可以解耦组件,提高组件复用性。
本文包括两部分:
- 介绍
IOC
概念 IOC
在React
中的应用
IOC是什么
控制反转(Inversion of Control
,缩写为IoC),是面向对象编程中的一种设计原则,可以用来减低计算机代码之间的耦合度,其中最常见的方式就是依赖注入(Dependency Injection
,简称DI)。
通过控制反转,对象在被创建的时候,由一个调控系统内所有对象的外界实体将其所依赖的对象的引用传递给它。也可以说,依赖被注入到对象中。
一般这个概念在 Java 中提的比较多,但是在前端领域,似乎很少会提到这个概念,其实用好这个思想无论在前后端一样可以帮助我们的组件解耦。
“依赖注入”实际上就是对“依赖倒置原则”的一种体现。
依赖倒置原则(Dependency Inversion):
- 上层模块不应该依赖底层模块,它们应该依赖于共同的抽象。
- 抽象不应该依赖于细节,细节应该依赖于抽象。
让我们来看个例子:
我们有个士兵的类,在类内部会实例化一种武器:
士兵的武器应该是多种多样的,但是在Soldier
类内部依赖了Rifle
。
所以当我们想将武器从步枪换为手榴弹时,只能这样改写:
理想的状态是:士兵不依赖具体的武器,弹药库里有什么武器,就用什么武器。
在这种情况下,IOC
作为弹药库,就派上了用场。
让我们来改写代码:
第一步:DI(Dependency Injection)
改写的第一步是使士兵不依赖具体的武器,而是将武器作为依赖注入给士兵:
我们将武器的实例作为Soldier
的参数传入,于是可以如下调用:
这一步被称为DI
(依赖注入)。
第二步:IOC容器
那么武器从哪儿来呢?接下来来打造我们的武器库:
武器库支持存武器(setWeapon
)和取武器(getWeapon
)。
现在,士兵不依赖具体武器,只需要去武器库取武器:
改造前的依赖关系:
士兵 --> 武器
改造前原先应用(士兵)拥有依赖的完全控制权。
改造后的依赖关系:
士兵 --> 武器库 <-- 武器
改造后应用(士兵)与服务提供方(武器)解耦,他们通过IOC
容器(武器库)联系。
从Demo
也能看出IOC
与DI
的关系:DI
是实现IOC
编程思想的一种方式。
除了DI
外,另一种实现方式是Dependency Lookup
(依赖查找),简称DL
。
为啥需要依赖注入?
依赖注入(更广泛地说就是控制反转)主要用来解决下面几个问题:
- 模块解耦 – 在代码设计中应用,强制保持代码模块分离。
- 更好的可复用性 – 让模块复用更加容易。
- 更好的可测试性 – 通过注入模拟依赖可以更方便测试。
其实, React 本身也内置了对依赖注入的支持。
IOC与React
在React
中,为组件传递的props
就是一种DI
实现。
为了跨层级传递数据,我们常使用Context API
:
context
将依赖提供方(name
)与依赖使用方(Name
)隔离,可以看作是一种IOC
实现。
所以说,合理使用React
可以充分利用IOC
的思想解耦代码逻辑。
接下来我们看看专业的DI
库如何与React
结合:
InversifyJS
InversifyJS[1]是一个强大、轻量的DI
库。
首先我们实现依赖(武器的实现):
通过inversify
提供的injectable decorator
标记该class
是可被注入的。
接下来实现需求方(士兵的实现):
IOC\
至此,完成一个React
组件的简单IOC
。
业务逻辑的更多依赖都可以通过注入IOC容器来实现解耦。
下面我们再来看看几个 InversifyJS 的扩展库。
inversify-inject-decorators
该工具库主要提供了 lazyInject
之类的方法,它可以给出了一个惰性的注入,意思是在对象初始化时不需要提供依赖,当我们没办法改构造函数时,这个库就派上用场啦。
另外,除了字面上所说的惰性,另外一个非常重要的功能就是允许你将 inversifyJs
集成到任何自己控制类实例创建的库或者框架,比如 React
。
下面是一个 @lazyInject
的使用示例:
inversify-react
inversify-react
是一个唯一执行依赖注入的库。就像使用 React Context.Provider
一样,我们从这个库也能拿到一个 Provider
:
然后我们就能在子组件中使用依赖了:
react-inversify
虽然和上一个库名字很像,但是两个库的做法是不一样的,这种方法更接近于 React 的思想,因为对象是作为属性传递的,而不是在组件内部实例化。
结实好友
喜欢结交一些和我一样处于前端进阶的朋友,有时候一个人的路不好走,多人一起走才有意思。链接