Skip to content

IOC 控制反转()

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

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

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

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

js
// 订单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类的文件都需要修改,

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

js
// 订单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 容器.
js
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().