前言
nestjs是一个功能强大的node框架。其中有很多编程概念,例如:依赖注入(DI)系统、面向切面编程(AOP)支持。本文将带大家了解 IOC 与 DI 的概念。
IOC 与 DI
控制反转(英语:Inversion of Control,缩写为IoC),是面向对象编程中的一种设计原则,可以用来减低计算机代码之间的耦合度。
其中最常见的方式叫做依赖注入(Dependency Injection,简称DI)。
现在我们有一个简单的需求,我们有多个类,需要一个方法,可以获取某个类的实例,从而访问改类内部的方法。
比如,我们需要实现下面这段代码的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| class A { b: B; } class B { c: C; } class C { hello() { console.log('hello world'); } }
const container = new Container(); const a = container.get(A);
|
第一步
我们在上面的例子里看到了 Container 类,首先我们先得知道 Container 需要做什么。
我们觉得它需要分析 A 这个类的属性中依赖了哪些东西,我们一眼就能看出 A 依赖了 B,而 B 依赖了 C,但是程序呢?
为了简化一下,这个地方需要有一个假设,假如我们的属性都是标准的类好了,现在就简单了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| class Container { get(Module) { const obj = new Module(); const properties = Object.getOwnPropertyNames(obj); for(let p of properties) { if (!obj[p]) { if (p === 'b') { obj[p] = this.get(B); } else if (p === 'c') { obj[p] = this.get(C); } else {} } } } }
|
我们使用递归的方式不断的get某一个类的实例,如果对象不存在,则不断的进行创建
第二步
现在我们只有3个类,核心功能很快就完成了,下一步我们来想想怎么优化
如果有另一个类,包裹了其他的几个实例怎么办呢。
1 2 3 4 5 6 7 8 9 10 11
| class A { b: B; d: D; } class B { c: C; } class D { b: B; c: C; }
|
按照我们的核心代码,估计会创建出好几个B实例和C实例。
那么,开始优化吧,我们先加个缓存吧。
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
| class Container { cachae = {}; getName(Module) { return Module.Name.toLowerCase(); } get(Module) { if (this.cachae[this.getName(Module)]) { return this.cachae[this.getName(Module)]; } const obj = new Module(); this.cachae[this.getName(Module)] = obj; const properties = Object.getOwnPropertyNames(obj); for(let p of properties) { if(!obj[p]) { if(p === 'b') { obj[p] = this.get(B); } else if(p === 'c') { obj[p] = this.get(C); } else if(p === 'd') { obj[p] = this.get(D); } else {} } } } }
|
经过了缓存,我们可以保证在一次运行中,每个类都只会有一个对象实例存在了。
第三步
我们现在只能创建A、B、C、D、这四个类,是不是有点不太够。
没关系,我们这就教你支持任意的类。
聪明的人类想出了一个方法,如果我提前就知道了有哪些类,存在一个表里(Map)里,
下次用的时候如果名字一样就能匹配上了呢。
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 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
| const globby = require('globby'); const path = require('path');
class Container {
cwd = process.cwd(); cache = {}; classTable = {};
init() { const fileResults = globby.sync(['**/**.ts', '**/**.js'], { cwd: this.cwd, ignore: [ '**/node_modules/**', ], });
for (const name of fileResults) { const exports = require(this.cwd + '/' + name); this.classTable[this.getName(exports)] = exports; } }
getName(Module) { return Module.name.toLowerCase(); }
get(Module) { if(this.cache[this.getName(Module)]) { return this.cache[this.getName(Module)]; }
const obj = new Module(); this.cache[this.getName(Module)] = obj; const properties = Object.getOwnPropertyNames(obj); for(let p of properties) { if(!obj[p]) { if(this.classTable[p]) { obj[p] = this.get(this.classTable[p]); } } } } }
|