59

我使用单独的路由器文件作为主应用程序和身份验证应用程序的模块。我无法获得将变量(数据库客户端)传递到路由器的最佳方法。我不想对其进行硬编码或将其传递给:

module.exports = function(app, db) {

也许这是使用单例寄存器或使用全局数据库变量的最佳方式?

你对设计模式有什么经验?哪种方式最好,为什么?

4

4 回答 4

109

我发现使用依赖注入来传递东西是最好的风格。它确实看起来像你:

// App.js
module.exports = function App() {
};

// Database.js
module.exports = function Database(configuration) {
};

// Routes.js
module.exports = function Routes(app, database) {
};

// server.js: composition root
var App = require("./App");
var Database = require("./Database");
var Routes = require("./Routes");
var dbConfig = require("./dbconfig.json");

var app = new App();
var database = new Database(dbConfig);
var routes = new Routes(app, database);

// Use routes.

这有很多好处:

  • 它迫使您将系统分成具有明确依赖关系的组件,而不是将依赖关系隐藏在它们调用的文件中间的某个地方require("databaseSingleton")或更糟的地方,global.database.
  • 它使单元测试变得非常容易:如果我想单独测试Routes,我可以用 fakeappdatabaseparams 注入它,只测试Routes代码本身。
  • 它将所有对象图连接放在一个地方,即组合根(在本例中是server.js应用程序入口点)。这使您可以在一个地方查看所有内容如何在系统中组合在一起。

我见过的对此的更好解释之一是对 Mark Seeman 的采访,他是优秀著作.NET 中的依赖注入的作者。它同样适用于 JavaScript,尤其是 Node.js:require通常用作经典的服务定位器,而不仅仅是模块系统。

于 2012-04-26T06:31:32.070 回答
2

我建议您使用 db 实例和其他需要全局使用的东西(如“单例”)创建一个设置文件。

例如,我的 redis db 客户端有 settings.js:

var redis = require('redis');
exports.redis = redis.createClient(6379, '127.0.0.1');

在其他多个模块中,我将其包括在内:

var settings = require('./settings');
setting.redis.<...>

很多时候包括它我总是有一个数据库连接实例。

于 2012-04-25T13:13:08.587 回答
1

如果您使用依赖注入框架,您可以为自己节省所有连接模块的样板代码

这个答案列出了其中一些。我还在这里构建了一个更简单的 DI 框架

编辑:如果页面发生变化,下面是答案的副本


require是Node.js 中管理依赖项方式,当然它是直观和有效的,但它也有其局限性。

我的建议是看一下目前可供 Node.js 使用的一些依赖注入容器,以了解它们的优缺点。他们之中有一些是:

仅举几个。

现在真正的问题是,与简单的require.

优点:

  • 更好的可测试性:模块接受它们的依赖作为输入
  • 控制反转:决定如何在不触及应用程序主代码的情况下连接模块。
  • 用于解析模块的可定制算法:依赖项具有“虚拟”标识符,通常它们不绑定到文件系统上的路径。
  • 更好的可扩展性:由 IoC 和“虚拟”标识符启用。
  • 其他可能的花哨的东西:
    • 异步初始化
    • 模块生命周期管理
    • DI 容器本身的可扩展性
    • 可以轻松实现更高级别的抽象(例如 AOP)

缺点:

  • 与 Node.js 的“体验”不同:不使用require绝对感觉像是在偏离 Node 的思维方式。
  • 依赖项与其实现之间的关系并不总是明确的。依赖关系可以在运行时解决并受各种参数的影响。代码变得更难理解和调试
  • 启动时间较慢
  • 成熟度(目前):目前没有一个解决方案真正流行,所以没有那么多教程,没有生态系统,没有经过实战测试。
  • 一些 DI 容器不能很好地与 Browserify 和 Webpack 等模块捆绑器配合使用。
于 2016-06-25T14:34:29.807 回答
0

它已完全过时,但您可以global 在脚本中使用:

 global.foo = new Foo();

在另一个脚本中:

 foo.bar();

您还可以使用已经存在的常量:

 Object.foo = new Foo();

和这里 :

 Object.foo.bar();
于 2014-02-23T16:55:11.060 回答