我目前正在用nodejs创建一些实验性项目。我用Spring编写过很多Java EE web应用程序,非常欣赏依赖注入的便利性。
现在我很好奇:如何对节点进行依赖注入?或者:我真的需要它吗?因为编程风格不同,是否存在替代概念?
到目前为止,我谈论的是一些简单的事情,比如共享数据库连接对象,但我还没有找到一个让我满意的解决方案。
我目前正在用nodejs创建一些实验性项目。我用Spring编写过很多Java EE web应用程序,非常欣赏依赖注入的便利性。
现在我很好奇:如何对节点进行依赖注入?或者:我真的需要它吗?因为编程风格不同,是否存在替代概念?
到目前为止,我谈论的是一些简单的事情,比如共享数据库连接对象,但我还没有找到一个让我满意的解决方案。
当前回答
看看dips(一个简单而强大的Node.js依赖注入和实体(文件)管理框架)
https://github.com/devcrust/node-dips
其他回答
我自己调查过了。我不喜欢引入神奇的依赖utils库,它提供了劫持模块导入的机制。相反,我为我的团队提出了一个“设计指南”,相当明确地说明可以通过在模块中引入工厂函数导出来模拟哪些依赖项。
我大量使用ES6的参数和解构功能,以避免一些样板文件,并提供一个命名依赖项覆盖机制。
这里有一个例子:
import foo from './utils/foo';
import bob from './utils/bob';
// We export a factory which accepts our dependencies.
export const factory = (dependencies = {}) => {
const {
// The 'bob' dependency. We default to the standard 'bob' imp if not provided.
$bob = bob,
// Instead of exposing the whole 'foo' api, we only provide a mechanism
// with which to override the specific part of foo we care about.
$doSomething = foo.doSomething // defaults to standard imp if none provided.
} = dependencies;
return function bar() {
return $bob($doSomething());
}
}
// The default implementation, which would end up using default deps.
export default factory();
这是它的用法的一个例子
import { factory } from './bar';
const underTest = factory({ $bob: () => 'BOB!' }); // only override bob!
const result = underTest();
对于不熟悉ES6的人,请原谅它的语法。
事实上,你可以在没有IoC容器的情况下测试node.js,因为JavaScript是一种真正动态的编程语言,你可以在运行时修改几乎所有东西。
考虑以下几点:
import UserRepository from "./dal/user_repository";
class UserController {
constructor() {
this._repository = new UserRepository();
}
getUsers() {
this._repository.getAll();
}
}
export default UserController;
因此,您可以在运行时覆盖组件之间的耦合。我认为我们应该以解耦JavaScript模块为目标。
实现真正解耦的唯一方法是删除对UserRepository的引用:
class UserController {
constructor(userRepository) {
this._repository = userRepository;
}
getUsers() {
this._repository.getAll();
}
}
export default UserController;
这意味着你需要在其他地方进行对象合成:
import UserRepository from "./dal/user_repository";
import UserController from "./dal/user_controller";
export default new UserController(new UserRepository());
我喜欢将对象组合委托给IoC容器的想法。您可以在文章《JavaScript中依赖倒置的当前状态》中了解更多关于此思想的内容。这篇文章试图揭穿一些“JavaScript IoC容器神话”:
误解1:JavaScript中没有IoC容器的位置 误解2:我们不需要IoC容器,我们已经有模块加载器了! 误解3:依赖倒置===注入依赖
如果你也喜欢使用IoC容器的想法,你可以看看InversifyJS。最新版本(2.0.0)支持多种用例:
内核模块 内核的中间件 使用类、字符串或符号作为依赖标识符 常数值注入 类构造函数的注入 工厂注入 汽车工厂 提供程序的注入(异步工厂) 激活处理程序(用于注入代理) 多注射 标记绑定 自定义标记装饰器 指定绑定 上下文绑定 友好的异常(例如循环依赖)
你可以在InversifyJS上了解更多。
我最近创建了一个名为circuitbox的库,它允许你在node.js中使用依赖注入。与我见过的许多基于依赖项查找的库相比,它实现了真正的依赖项注入。Circuitbox还支持异步创建和初始化例程。下面是一个例子:
假设下面的代码位于名为consoleMessagePrinter.js的文件中
'use strict';
// Our console message printer
// deps is injected by circuitbox with the dependencies
function ConsoleMessagePrinter(deps) {
return {
print: function () {
console.log(deps.messageSource.message());
}
};
}
module.exports = ConsoleMessagePrinter;
假设main.js文件中包含以下内容
'use strict';
// our simple message source
// deps is injected by circuitbox with the dependencies
var simpleMessageSource = function (deps) {
return {
message: function () {
return deps.message;
}
};
};
// require circuitbox
var circuitbox = require('../lib');
// create a circuitbox
circuitbox.create({
modules: [
function (registry) {
// the message to be used
registry.for('message').use('This is the message');
// define the message source
registry.for('messageSource').use(simpleMessageSource)
.dependsOn('message');
// define the message printer - does a module.require internally
registry.for('messagePrinter').requires('./consoleMessagePrinter')
.dependsOn('messageSource');
}
]
}).done(function (cbx) {
// get the message printer and print a message
cbx.get('messagePrinter').done(function (printer) {
printer.print();
}, function (err) {
console.log('Could not recieve a printer');
return;
});
}, function (err) {
console.log('Could not create circuitbox');
});
Circuitbox允许您定义组件并将它们的依赖项声明为模块。一旦初始化,它允许您检索组件。Circuitbox自动注入目标组件所需的所有组件,并将其提供给您使用。
该项目处于alpha版本。欢迎您的评论、想法和反馈。
希望能有所帮助!
Node.js和其他平台一样需要DI。如果您正在构建一些大的东西,DI将使您更容易模拟代码的依赖关系并彻底测试代码。
例如,数据库层模块不应该只在业务代码模块中使用,因为在单元测试这些业务代码模块时,dao将加载并连接到数据库。
一种解决方案是将依赖项作为模块参数传递:
module.exports = function (dep1, dep2) {
// private methods
return {
// public methods
test: function(){...}
}
}
通过这种方式,依赖关系可以轻松自然地模拟,你可以专注于测试你的代码,而不需要使用任何棘手的第三方库。
还有其他的解决方案(百老汇,建筑师等)可以帮助你解决这个问题。尽管他们可能做的比你想要的多,或者使用更多的杂物。
我一直很喜欢IoC的简单理念——“你不需要了解任何环境,需要的时候有人会叫你。”
但是我看到的所有IoC实现都完全相反——它们用更多的东西使代码变得混乱。所以,我创建了我自己的IoC,就像我想要的那样-它保持隐藏和不可见的90%的时间。
它用于MonoJS web框架http://monojs.org
我说的是简单的事情,比如共享一个数据库连接对象 但我还没有找到一个让我满意的解决办法。
它是这样做的——在配置中注册组件一次。
app.register 'db', ->
require('mongodb').connect config.dbPath
可以在任何地方使用
app.db.findSomething()
你可以在https://github.com/sinizinairina/mono/blob/master/mono.coffee这里看到完整的组件定义代码(包括DB Connection和其他组件)
这是你必须告诉IoC该做什么的唯一地方,之后所有这些组件都将自动创建和连接,你不再需要在应用程序中看到IoC特定的代码。
国际奥委会本身https://github.com/alexeypetrushin/miconjs