如何在Nodejs中进行有效的依赖注入?

时间:2016-02-16 21:50:36

标签: javascript node.js dependency-injection

让我们从一些参考代码开始

var express = require('express');  
var app = express();  
var session = require('express-session');

app.use(session({  
  store: require('connect-session-knex')()
}));

我在这里有几个问题,如果你知道答案,我想回答:

每次在Nodejs中调用require时,该命名依赖注入是什么?或依赖注入的真正含义是什么?

我之所以这样问,是因为我一直在阅读关于Node的内容,而且我看到有人在谈论modulemodule.export模式,我很困惑, moduledependency相同?

所以,我需要的是关于依赖注入的明确解释,以及何时/何时需要注入依赖项。 。

3 个答案:

答案 0 :(得分:8)

依赖注入与正常的模块设计有些相反。在正常的模块设计中,模块使用require()加载所需的所有其他模块,目的是使调用者能够轻松使用您的模块。调用者可以只在模块中require(),模块将加载所需的所有其他内容。

使用依赖注入,而不是模块加载它需要的东西,调用者需要传入模块所需的东西(通常是对象)。这可以使某些类型的测试更容易,并且可以更容易地模拟某些事情以便于测试。

  

每次在Nodejs中调用require时,都是指定的依赖项   注射?或依赖注入的真正含义是什么?

没有。当一个模块执行require()加载它自己的依赖注入而非依赖注入时。

  

我之所以这样问,是因为我一直在阅读   节点,我看到有人在谈论模块或   module.export模式,我很困惑,模块是一样的   依赖

模块与依赖项不同。正常的模块设计允许您require()只是模块,并返回一系列可以使用的导出。模块本身处理其自身依赖项的加载(通常使用模块内部的require()。)

以下是一些讨论使用依赖注入的一些优点/缺点的文章。最好的我可以说主要的优点是通过允许更容易模拟依赖对象(如数据库)来简化单元测试。

When to use Dependency Injection

When is it not appropriate to use dependency injection

Why should we use dependency injection

使用依赖注入的经典案例是模块依赖于数据库接口。如果模块加载它自己的数据库,那么该模块基本上是硬连接到该特定数据库。模块内置的体系结构不允许调用者指定应使用的存储类型。

但是,如果设置了模块,那么当调用者加载并初始化模块时,它必须传入一个实现特定数据库API的对象,然后调用者可以自由决定应该使用哪种类型的数据库。可以使用符合API合同的任何数据库。但是,调用者负担选择和加载特定数据库的负担。也可能存在混合情况,其中模块具有默认使用的内置数据库,但调用者可以提供他们自己的对象,如果在模块构造函数或模块初始化中提供它将使用它。

答案 1 :(得分:2)

想象一下这段代码。

var someModule = require('pathToSomeModule');

someModule();

在这里,我们不依赖于名称,而是依赖于该文件的路径。我们每次都使用SAME文件。

让我们看看角度的方式(对于客户,我知道,请耐心等待)

app.controller('someCtrl', function($scope) {
  $scope.foo = 'bar';
});

我知道客户端js没有文件导入/导出,但它是你应该看的基本概念。这个控制器没有指定$ scope变量ACTUALLY是什么,它只知道angular给它一些CALLED $ scope。

这是控制反转 就像说,别打电话给我,我会打电话给你

现在让我们用类似服务容器的东西来实现我们的原始代码(对此有许多不同的解决方案,容器不是唯一的选择)

// The only require statement
var container = require('pathToContainer')

var someModule = container.resolve('someModule');

someModule();

我们在这里取得了什么成就?现在,我们只需知道一件事,即容器(或您选择的任何抽象)。我们不知道someModule实际上是什么,或者它的源文件在哪里,只是它是我们从容器中得到的。这样做的好处是,如果我们想要使用someModule的不同实现,只要它符合与orignal相同的API,我们就可以在ENTIRE应用中将其替换为一个位置。容器。现在,每个调用someModule的模块都将获得新的实现。这个想法是,当你创建一个模块时,你定义了用来与它交互的a​​pi。如果不同的实现都符合单个api(或者你编写符合它的适配器),那么你可以换掉像脏内衣这样的实现,你的应用就可以了。

这种做法并不适合所有人,有些人讨厌在集装箱周围徘徊。

我个人认为,我宁愿编写一个接口(实现之间的一致api)而不是具体实现的代码。

node.js中的Dependency Injection 的实际示例

// In a file, far, far, away
module.exports = function(dependencyA, dependencyB) {
  dependencyA();
  dependencyB();
}

// In another file, the `caller`
// This is where the actual, concrete implementation is stored
var depA = someConcreteImplementation;
var depB = someOtherConcreteImplementation;

var someModule = require('pathToSomeModule');

someModule(depA, depB);

这样做的缺点是,现在调用者需要知道你的依赖是什么。有些人对此感到欣慰,喜欢它,其他人则认为这很麻烦。

我个人更喜欢这种方法。

如果您没有使用babel,或者在幕后更改功能的内容,您可以使用此方法获取角度样式参数解析

http://krasimirtsonev.com/blog/article/Dependency-injection-in-JavaScript

然后你可以解析从require得到的函数,而不是使用容器。

答案 2 :(得分:0)

我将injection-jsts-node

一起使用

npm i -P injection-js

它基于angular2 injector,因此注入非常简单。

以下是自述文件中的打字稿版本。

import 'reflect-metadata';
import { ReflectiveInjector, Injectable, Injector } from 'injection-js';

class Http {}

@Injectable()
class Service {
  constructor(private http: Http) {}
}

@Injectable()
class Service2 {
  constructor(private injector: Injector) {}

  getService(): void {
    console.log(this.injector.get(Service) instanceof Service);
  }

  createChildInjector(): void {
    const childInjector = ReflectiveInjector.resolveAndCreate([
      Service
    ], this.injector);
  }
}

const injector = ReflectiveInjector.resolveAndCreate([
  Service,
  Http
]);

console.log(injector.get(Service) instanceof Service);