了解Node.js Nestjs框架的模块机制,聊聊实现原理( 二 )


初期 , 我们在实现应用的时候 , 在满足当时需求的情况下 , 就会实现出 B 和 C 类的写法 , 这本身也没有什么问题 , 项目迭代了几年之后 , 都不一定会动这部分代码 。 我们要是去考虑后期扩展什么的 , 是会影响开发效率的 , 而且不一定派的上用场 。 所以大部分时候 , 我们都是遇到需要抽象的场景 , 再对部分代码做抽象改造 。
// 改造前class B{ contructor(){ this.a = new A(); }}new B()// 改造后class D{ contructor(a){ this.a = a; }}new D(new A())new D(new X())按照目前的开发模式 , CBD三种类都会存在 , B 和 C有一定的几率发展成为 D , 每次升级 D 的抽象过程 , 我们会需要重构代码 , 这是一种实现成本 。
这里举这个例子是想说明 , 在一个没有任何约束或者规定的开发模式下 。 我们是可以自由的写代码来达到各种类与类之间依赖控制 。 在一个完全开放的环境里 , 是非常自由的 , 这是一个刀耕火种的原始时代 。 由于没有一个固定的代码开发模式 , 没有一个最高行动纲领 , 随着不同开发人员的介入或者说同一个开发者不同时间段写代码的差别 , 代码在增长的过程中 , 依赖关系会变得非常不清晰 , 该共享的实例可能被多次实例化 , 浪费内存 。 从代码中 , 很难看清楚一个完整的依赖关系结构 , 代码可能会变得非常难以维护 。

了解Node.js Nestjs框架的模块机制,聊聊实现原理

文章插图

那我们每定义一个类 , 都按照依赖注入的方式来写 , 都写成 D 这样的 , 那 C 和 B 的抽象过程就被提前了 , 这样后期扩展也比较方便 , 减少了改造成本 。 所以把这叫All in 依赖注入 , 也就是我们所有依赖都通过依赖注入的方式实现 。
可这样前期的实现成本又变高了 , 很难在团队协作中达到统一并且坚持下去 , 最终可能会落地失败 , 这也可以被定义为是一种过度设计 , 因为额外的实现成本 , 不一定能带来收益 。
2.3 控制反转
既然已经约定好了统一使用依赖注入的方式 , 那是否可以通过框架的底层封装 , 实现一个底层控制器 , 约定一个依赖配置规则 , 控制器根据我们定义的依赖配置来控制实例化过程和依赖共享 , 帮助我们实现类管理 。 这样的设计模式就叫控制反转 。
控制反转可能第一次听说的时候会很难理解 , 控制指的什么?反转了啥?
猜测是由于开发者一开始就用此类框架 , 并没有体验过上个“Express、Koa时代” , 缺乏旧社会毒打 。 加上这反转的用词 , 在程序中显得非常的抽象 , 难以望文生义 。
前文我们说的实现 Koa 应用 , 所有的类完全由我们自由控制的 , 所以可以看作是一个常规的程序控制方式 , 那就叫它:控制正转 。 而我们使用 Nest , 它底层实现一套控制器 , 我们只需要在实际开发过程中 , 按照约定写配置代码 , 框架程序就会帮我们管理类的依赖注入 , 所以就把它叫作:控制反转 。
本质就是把程序的实现过程交给框架程序去统一管理 , 控制权从开发者 , 交给了框架程序 。
控制正转:开发者纯手动控制程序
了解Node.js Nestjs框架的模块机制,聊聊实现原理

文章插图

控制反转:框架程序控制
了解Node.js Nestjs框架的模块机制,聊聊实现原理

文章插图

举个现实的例子 , 一个人本来是自己开车去上班的 , 他的目的就是到达公司 。 它自己开车 , 自己控制路线 。 而如果交出开车的控制权 , 就是去赶公交 , 他只需要选择一个对应的班车就可以到达公司了 。 单从控制来说 , 人就是被解放出来了 , 只需要记住坐那趟公交就行了 , 犯错的几率也小了 , 人也轻松了不少 。 公交系统就是控制器 , 公交线路就是约定配置 。
通过如上的实际对比 , 我想应该有点能理解控制反转了 。
2.4 小结
从 Koa 到 Nest , 从前端的 JQuery 到 Vue React 。 其实都是一步步通过框架封装 , 去解决上个时代低效率的问题 。
上面的 Koa 应用开发 , 通过非常原始的方式去控制依赖和实例化 , 就类似于前端中的 JQuery 操作 dom , 这种很原始的方式就把它叫控制正转 , 而 Vue React 就好似 Nest 提供了一层程序控制器 , 他们可以都叫控制反转 。 这也是个人理解 , 如果有问题期望大神指出 。
下面再来说说 Nest 中的模块 @Module , 依赖注入、控制反转需要它作为媒介 。

推荐阅读