以前经常看到ioc(控制反转),今天有时间仔细整理了一下相关概念,在此记录一下吧。
首先,ioc在在使用中要保持几个原则
1、抽象不应该依赖具体,而具体应该依赖抽象。
2、高层对象不应该依赖底层对象,而都应该依赖于抽象。
3、不要面向需求编程。这样会导致代码的可扩展性差。
举个例子:
假设我们此时需要一个创建一辆汽车
class car { constructor() { this.itemHeight = '150'; // 高度 this.itemWight = '200'; // 宽度 this.length = '400'; // 长度 this.tyre = new tyreNOSlip(); } } class tyreNOSlip { constructor() { this.itemRadius = '150'; // 轮胎半径 this.itemWight = '30'; // 宽度 this.itemNonSlip = 'NO'; // 是否防滑 } } const carItem = new car();
假设此时我想替换一个新的汽车轮胎,想要拥有防滑功能,那么我们该怎么办呢?首先需要新建一个tyreYesSlip类用来当做带有防滑功能的轮胎。同时需要修改car内部代码改写成想要的类。
class car { constructor() { this.itemHeight = '150'; // 高度 this.itemWight = '200'; // 宽度 this.length = '400'; // 长度 this.tyre = new tyreYesSlip(); } }class tyreYesSlip { constructor() { this.itemRadius = '150'; // 轮胎半径 this.itemWight = '30'; // 宽度 this.itemNonSlip = 'YES'; // 是否防滑 } } const carItem = new car();
这样实际上有很大的问题。我们仅仅要修改的是轮胎,为什么要把汽车的类也更改呢。如果要是以后有很多不同的轮胎,每次都要修改最上层的类,这样不利于项目的扩展和维护,所以我们可以利用ioc的思想将其修改一下
class car { constructor() { this.itemHeight = '150'; // 高度 this.itemWight = '200'; // 宽度 this.length = '400'; // 长度 this.tyre = getTyre.getItem(); } } class getTyre { constructor() {} static getItem(type) {return new tyreYesSlip(); } } class tyreNOSlip { constructor() { this.itemRadius = '150'; // 轮胎半径 this.itemWight = '30'; // 宽度 this.itemNonSlip = 'NO'; // 是否防滑 } } class tyreYesSlip { constructor() { this.itemRadius = '150'; // 轮胎半径 this.itemWight = '30'; // 宽度 this.itemNonSlip = 'YES'; // 是否防滑 } } const carItem = new car(); console.log(carItem);
我们将获取轮胎的方式提取出来,这样我们可以直接通过getTyre类中的代码来控制我们想要的轮胎类型。而同时它也算是一个非常简陋的ioc容器。
其实在我眼里,ioc这种方式存在的必要性是想提醒我们代码解耦的重要性,将代码的各司其职发挥到极致,模块的可扩展性和适用性都要强。
话说回来这种思想在很多框架中都可以找到相应的实践。例如koa的方法中App.use()就是以依赖注入(DI)这种方式实现的。
举个例子,koa-bodyparser可以控制处理的json数据的长度,默认最大为1mb,如果我们需要修改只需要修改在注入使用bodyparser的时候的参数就可以了,并没有必要修改koa的代码。准确说koa就是一个容器,我们通过app.use去添加不同的中间件,为它赋值更多的功能。每个不同的中间件负责不同的功能,修改问题时修改对应的中间件模块就好,不需要修改koa的源代码。
最后再记录一下DI和IOC的区分,依赖注入(DI)只是控制反转(IOC)思想的一种实现方式。