IOC 控制反转()

控制反转 (inversion of control, 缩写 IOC), 是一种设计原则, 顾名思义,他是用于面向对象设计中的各种控件.

可以降低代码的耦合度, 使程序模块化,易于扩展, IOC意味着将依赖模块和被依赖模块都交给容器去管理,当我们使用模块的时候,由容器动态的将它的依赖关系注入到模块中,

正对上面的问题, 往往需要IOC模式, 一般使用DI(Dependency Injection) 依赖注入的方式实现.

以订单为例子, 先看美誉依赖注入的情况.

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
// 订单model类, models/order.js
class Order {
constructor() {}

insert() {
return true;
}
}

// 订单类 controllers/OrderController.js
const Order = require("./Order.js");

class OrderController {
constructor() {
this.order = new Order();
}

createOrder(...args) {
this.order.insert(...args);
}
}

// router/index.js
const OrderController = require("./OrderController.js");
const orderController = new OrderController();

上面是没有依赖注入的情况, OrderController 类耦合了Order类, 在使用前必须require 引入 Order类才能使用,假如Order类发生了修改变化, 那么所有依赖这个Order类的文件都需要修改,

来在看看依赖注入的情况:

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
// 订单model类, models/order.js
class Order {
constructor() {}

insert() {
return true;
}
}

// 订单类 controllers/OrderController.js
const Order = require("./Order.js");

class OrderController {
constructor(order) {
this.order = order;
}

createOrder(...args) {
this.order.insert(...args);
}
}

// router/index.js
const Order = require("../model/Order.js");
const OrderController = require("./OrderController.js");
const orderController = new OrderController(new Order());

可以看出依赖注入已经对模块进行了解耦,但是还是有不足之处, 下面总结一下依赖注入的优缺点:

优点: 通过依赖注入高层模块与底层模块耦合度降低,因此底层模块发生变化时,我们不需要了解底层模块发生的变化, 只需要管理router中依赖的路径.

不足:

我们所有依赖的模块都在router中引入,明显增加了router模块的复杂性, 我们需要一个专门管理注入及被注入的容器. 即我们常说的IOC 容器.
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
27
28
29
30
31
32
33
34
35
36
class IOC {
constructor() {
this.controller = new Map();
}

bind(key, callback) {
this.controller.set(key, { callback, single: false });
}

sigleton(key, callback) {
this.controller.set(key, { callback, single: true });
}

use(key) {
const item = this.controller.get(key);
if (!item) {
throw new Error("error");
}

if (item.single && !item.instance) {
item.instance = item.callback();
}

return item.single ? item.instance : item.callback();
}
}
// router/index.js
const Order = require("../model/Order.js");
const OrderController = require("./OrderController.js");

ioc.bind("order", (...args) => new Order(...args));
ioc.bind("orderController", (...args) => new OrderController(ioc.use("order")));

// router.js
const ioc = require("../ioc.js");
const orderController = ioc.use("orderController");

上面就是一个简单的ICO 容器实现, 通过bind方法将模块间的依赖绑定到容器中,通过use方法判断模块是否存在,
若不存在则报错;
存在则判断是否实例化,已实例化不需要执行的callback().