使用NodeJS进行惯用依赖注入

时间:2016-01-05 10:24:47

标签: javascript node.js dependency-injection

TLDR - 在NodeJS(ES2015)中更为惯用,为什么?

导出类,稍后实例化:

foo.js:

export default class Foo {
}

bar.js:

export default class Bar {
  constructor(foo) { this.foo = foo; }
}

app.js:

import Foo from './foo';
import Bar from './bar';

new Bar(new Foo());

导出实例 - 有效地让他们成为所有单身人士:

foo.js:

class Foo {
}

export default new Foo();

bar.js:

import foo from './foo';

class Bar {
}

export default new Bar();

app.js:

import bar from './bar';

我来自Java世界。在那里,DI框架通常会创建某种应用程序上下文" - 他们创建类的实例并将对象连接在一起,将它们放在一个大包中,您可以从中获得所需的任何内容。

静态"代码导入&#34>之间存在巨大差异。 (import com.acme.FooService,只导入类的定义)与获取可以调用的实例等。

在NodeJS中执行此操作的最常见,最合理的方法是什么? "导出实例"对我来说选项似乎很脏,感觉负责加载代码的基础设施也负责实例化对象并将它们连接在一起。或者这可能是一种错误的二分法,而这实际上是与Node相关的方式?

1 个答案:

答案 0 :(得分:1)

tl; dr :没有理由不在Node.js中应用依赖注入模式或导出类。

您怀疑导出类的主要原因 - 即使它们在您的应用程序中具有单例生命周期范围 - 是JavaScript的hacky,动态特性。

当然,如果不采用依赖注入模式,您实际上可以做很多事情。

例如,正如您所提到的,您可以导出类的实例,从而使它们成为所有单例。以这种方式导出的模块甚至不必是任何类的实例。请考虑以下代码段:

export class Foo {
    foo() {
    }

    bar() {
    }
}

export default new Foo();

export function foo() {
}

export function bar() {
}

因此,这是JavaScript生态系统中非常常见的方式。如果没有涉及内部模块 state ,那么在各方面都没关系。比如,一个公共接口由一堆函数组成的库(如jquery,lodash等)。但仍然存在一些问题,即某些类(示例中为Bar)负责获取自己的依赖项(Foo)。

你也可以使用像proxyquire这样的黑客攻击。这是人们在JavaScript中解决测试问题的常用方法。使用这些库,您可以拦截对require的调用(不确定是否支持ES6模块语法,如import)并提供您自己的模拟/存根以进行测试。

但是,随着应用程序变得越来越复杂和越来越大,DI可以帮助您保持源代码的可维护性。

我认为在JS中应用DI实践的主要问题是不成熟。目前,JavaScript中的DI解决方案都不是真正受欢迎,所以没有那么多指南,没有教程,没有基础设施。此外,许多声称是DI容器的JavaScript库要么用丑陋的注释污染模块,要么实际实现Service Locator反模式或任何东西。

所以,我认为,最合理的方法是:

  1. 尽可能导出课程。例外:您可以在应用程序Composition Root中将类重新导出为构造实例。
  2. 在非常复杂的情况下使用Pure DI(没有DI容器)或考虑使用一些DI容器(不过你必须google并做出深思熟虑的选择)。
  3. 避免过度工程。例如,不要犹豫,在应用程序或基础架构层中明确依赖一些具有稳定API或您自己的模块的外部库(hovewer,我建议您的域/业务逻辑层保持干净)
  4. 当然,与软件开发相关的任何事情一样,选择DI或导出单例取决于您的软件的复杂性和您的要求。