节点中的 JavaScript 依赖注入和 DIP:require 与构造函数注入

2023-12-04

我是来自 .NET 世界的 NodeJs 开发新手 我正在网上搜索在 Javascript 中重新分级 DI / DIP 的最佳实践

在 .NET 中,我会在构造函数中声明我的依赖项,而在 javascript 中,我看到一个常见的模式是通过 require 语句在模块级别声明依赖项。

对我来说,当我使用 require 时,我会耦合到一个特定的文件,而使用构造函数来接收我的依赖项则更灵活。

作为 javascript 的最佳实践,您建议做什么?(我正在寻找架构模式而不是IOC技术解决方案)

在网上搜索我发现了这篇博客文章(评论中有一些非常有趣的讨论):https://blog.risingstack.com/dependency-injection-in-node-js/

它很好地总结了我的冲突。 这是博客文章中的一些代码,可以帮助您理解我在说什么:

// team.js
var User = require('./user');

function getTeam(teamId) {  
  return User.find({teamId: teamId});
}

module.exports.getTeam = getTeam; 

一个简单的测试看起来像这样:

 // team.spec.js
    var Team = require('./team');  
    var User = require('./user');

    describe('Team', function() {  
      it('#getTeam', function* () {
        var users = [{id: 1, id: 2}];

        this.sandbox.stub(User, 'find', function() {
          return Promise.resolve(users);
        });

        var team = yield team.getTeam();

        expect(team).to.eql(users);
      });
    });

VS DI:

// team.js
function Team(options) {  
  this.options = options;
}

Team.prototype.getTeam = function(teamId) {  
  return this.options.User.find({teamId: teamId})
}

function create(options) {  
  return new Team(options);
}

test:

// team.spec.js
var Team = require('./team');

describe('Team', function() {  
  it('#getTeam', function* () {
    var users = [{id: 1, id: 2}];

    var fakeUser = {
      find: function() {
        return Promise.resolve(users);
      }
    };

    var team = Team.create({
      User: fakeUser
    });

    var team = yield team.getTeam();

    expect(team).to.eql(users);
  });
});

关于你的问题:我认为 JS 社区没有通用的做法。我在野外见过这两种类型,需要修改(比如rewire or 代理查询)和构造函数注入(通常使用专用的 DI 容器)。不过,我个人认为不使用 DI 容器更适合 JS。那是因为 JS 是一种动态语言发挥一等公民的作用。让我解释一下:

使用 DI 容器强制构造函数注入对于一切。它会产生巨大的配置开销,主要原因有两个:

  1. 在单元测试中提供模拟
  2. 创建对其环境一无所知的抽象组件

关于第一个论点:我不会仅仅为了单元测试而调整我的代码。如果它使你的代码更干净、更简单、更通用且不易出错,那么就放弃吧。但如果你唯一的原因是你的单元测试,我不会采取权衡。您可以通过需要修改和猴子修补。如果您发现自己编写了太多模拟,那么您可能根本不应该编写单元测试,而应该编写集成测试。埃里克·埃利奥特写过一篇很棒的文章关于这个问题。

关于第二个论点: 这是一个有效的论点。如果您想创建一个只关心接口而不关心实际实现的组件,我会选择简单的构造函数注入。然而,既然 JS 并不强迫你在所有事情上都使用类,为什么不直接使用函数呢?

In 函数式编程,将有状态 IO 与实际处理分开是一种常见的范例。例如,如果您正在编写应该计算文件夹中文件类型的代码,则可以这样写(特别是当他/她来自一种到处强制执行类的语言时):

const fs = require("fs");

class FileTypeCounter {
    countFileTypes(dirname, callback) {
        fs.readdir(dirname, function (err) {
            if (err) return callback(err);
            // recursively walk all folders and count file types
            // ...
            callback(null, fileTypes);
        });
    }
}

现在,如果您想测试它,您需要更改代码以注入假的fs module:

class FileTypeCounter {
    constructor(fs) {
        this.fs = fs;
    }
    countFileTypes(dirname, callback) {
        this.fs.readdir(dirname, function (err) {
            // ...
        });
    }
}

现在,每个使用你的类的人都需要注入fs进入构造函数。由于这很无聊,并且一旦您拥有较长的依赖关系图,就会使您的代码变得更加复杂,因此开发人员发明了 DI 容器,他们可以在其中配置内容,然后 DI 容器会计算出实例化。

但是,只编写纯函数怎么样?

function fileTypeCounter(allFiles) {
    // count file types
    return fileTypes;
}

function getAllFilesInDir(dirname, callback) {
    // recursively walk all folders and collect all files
    // ...
    callback(null, allFiles);
}

// now let's compose both functions
function getAllFileTypesInDir(dirname, callback) {
    getAllFilesInDir(dirname, (err, allFiles) => {
        callback(err, !err && fileTypeCounter(allFiles));
    });
}

现在您拥有两个开箱即用的超级多功能函数,一个用于执行 IO,另一个用于处理数据。fileTypeCounter is a 纯函数并且超级容易测试。getAllFilesInDir是不纯粹的,但这是一个很常见的任务,你经常会发现它已经在npm其他人已经为其编写了集成测试。getAllFileTypesInDir只需用一点控制流来组合您的函数即可。这是集成测试的典型案例,您希望确保整个应用程序正常工作。

通过将 IO 和数据处理之间的代码分开,您将根本不需要注入任何内容。如果您不需要注射任何东西,这是一个好兆头。纯函数是最容易测试的东西,并且仍然是在项目之间共享代码的最简单方法。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

节点中的 JavaScript 依赖注入和 DIP:require 与构造函数注入 的相关文章

  • 如何在 C# 中知道 PID 和 VID 来发现虚拟 COM 端口名称

    如果我知道 PID 和 VID 我会尝试找到查找 COM 端口名称的正确方法 到目前为止 我写了解决方法 但我不相信没有更优雅和正确的方法 顺便说一句 我知道我可以使用 REGEX 编写此代码只是为了测试解决方法 我知道还有很大的改进空间
  • 将其作为参数传递给 addEventListener()

    我想添加change一组复选框的事件 我如何访问this在我的事件函数中 这样当我执行事件时我可以访问复选框的值 这是我当前的代码 var checkboxes document getElementsByClassName cb Arra
  • 使用 Javascript 对象模型在 SharePoint 任务上设置“分配给”

    我想创建一个共享点任务并将其分配给我自己 当前用户 在 javascript 对象模型中 我有下面的代码 但我认为我需要设置 spusercollection 对象 而不是设置特定用户 但是 我似乎无法在任何地方找到如何执行此操作的任何示例
  • 内联执行生成的汇编程序

    我正在阅读以下演示文稿 http wingolog org pub qc 2012 js slides pdf http wingolog org pub qc 2012 js slides pdf其中讨论了 4 10 19 内联 ASM
  • Javascript“命名空间”和 jQuery AJAX

    我正在使用此处列出的建议 http www odetocode com articles 473 aspx http www odetocode com articles 473 aspx 使用模拟的JavaScript AJAX网络聊天系
  • Azure Functions - 注入的 ILogger 日志未显示

    我在用着FunctionsStartup在 Azure Functions 项目中设置 IoC 绑定 但是 从注入创建的任何日志ILogger
  • 用dagger 2查看依赖注入

    我有一个自定义视图扩展TextView 我应该在哪里调用我的组件来注入视图 component inject customTextView 因此 我发现我需要在自定义视图的构造函数中添加注入 在所有视图中 或者使一个调用另一个 Exampl
  • React JS“this”没有按预期工作

    我有下面的代码 save function var this this console log this refs itemText this setState isEditing false function console log In
  • 试图使用加密来混淆我的项目打破了它

    我试图尝试不同的混淆选项 为了做到这一点 我首先尝试了加密货币 以下是我遵循的步骤 打开加密向导并选择一些选项 选择我的解决方案文件 完成向导后 我看到有些 Dll 被很好地混淆了 但我的项目现在无法构建 我注意到的两件事是 我的文件夹中有
  • 使用 jQuery 更改 SVG 元素的“xlink:href”属性

    我正在尝试使用单击事件更改 xlink href 属性 到目前为止它部分有效 这就是我正在做的 HTML a href class ui btn ui corner all ui shadow editIcon style text ali
  • 根据数据更改图例颜色高图表

    我可以根据数据动态设置列的颜色 但无法弄清楚如何更改图例中的颜色 请注意 jsfiddle 最新的条形图是绿色的 但图例是蓝色的 有没有办法改变列颜色也会改变图例颜色 这是我用于列颜色的代码 jsfiddle http jsfiddle n
  • 如何将输入字段值作为 URL 查询字符串传递,单击提交按钮将打开该字符串?

    我必须输入这样的字段
  • 如何与使用 child_process.spawn 创建的新创建的服务器交互

    我正在尝试为我的私人托管的 反恐精英全球攻势 服务器制作一个前端 当我点击运行服务器时 在前端 一切正常 服务器启动并记录到控制台 但是如何查看服务器IP地址 服务器中的玩家等信息呢 这是我到目前为止运行服务器的内容 router post
  • Bootstrap 3 - 模态背景不会根据模态对话框的高度调整大小?

    我将一个表单放入模式中 并尝试在用户触发单选按钮时显示表单的一些隐藏字段 显示隐藏字段后 模态自动重新缩放的高度 但模态背景的高度不能用作模态对话框 我该如何解决它 div class modal fade div class modal
  • 限制线的长度

    我正在尝试画一条代表 弹弓 的线 并且希望它具有最大拉伸长度 在 p5 中 我在位置和位置之间画了一条线 line posA x posA y posB x posB y posA 是鼠标 x 和 y posB 是画布上圆的位置 我想要做的
  • Firefox 和 Chrome 为 offsetTop 提供了不同的值

    我试图相对于输入字段定位一个跨度元素 让我们称之为 工具提示跨度 为此 我将工具提示跨度和输入字段包装在另一个跨度元素中 我们称之为 包装器跨度 该元素具有position relative 然后我设置position absolute在工
  • .parents() 没有 jquery - 或 querySelectorAll 为父母[重复]

    这个问题在这里已经有答案了 可能的重复 使用 matchesSelector js 检查 event target parentElement https stackoverflow com questions 12977658 check
  • 如何组合||条件语句中的运算符[重复]

    这个问题在这里已经有答案了 代替 if foo 1 foo 5 foo 9 我喜欢将它们组合起来 类似于以下内容 这不起作用 if foo 1 5 9 那可能吗 不幸的是不是 你最好的选择是创建一个扩展方法 public static bo
  • 保留对 React 状态变量的“引用”

    据我所知 Javascript 中没有指针 我有以下问题 但我想知道是否有一个解决方案让我无法解决 解决方案可能是普通的 Javascript 或者像 Context API 这样的 React js 钩子 useContext 或者更多
  • Chrome 中的 addEventListener

    我正在关注 Lynda com 上有关新 DOM 事件模型的教程 这是我正在使用的代码 function addEventHandler oNode sEvt fFunc bCapture if typeof window event un

随机推荐

  • 无法在我的 Mac Mountain Lion 上运行 Composer - openssl 扩展

    我安装了几个需要 Composer 的 CMS 两天以来我遇到了错误 无法继续 RuntimeException You must enable the openssl extension to download files via htt
  • Powershell 中的管道

    我正在阅读有关 PowerShell 中管道如何工作的信息 about 管道 并了解管道一次传送一个对象 So this Get Service Format Table Property Name DependentServices 与此
  • 将外键关系限制为相关子类型的行

    概述 我试图表示数据库中的几种类型的实体 这些实体具有许多共同的基本字段 然后每个实体都有一些不与其他类型的实体共享的附加字段 工作流程经常涉及将实体列出在一起 因此我决定使用一个包含其公共字段的表 然后每个实体将拥有自己的包含附加字段的表
  • 查找单击按钮的类

    我知道我已经问过这个问题很多次了 但我仍然没有找到正确的答案 num 只需要显示在单击按钮的类中
  • C#程序中TCP Socket没有停止接收数据

    Android 2 2 手机中的 Java Android 应用程序正在将字符串数据发送到 C 程序 C 程序接收数据并正确显示仅限第一次 然后它就不会停止接收数据 但由于没有数据 所以显示为0作为接收到的数据 在调试时并没有接收到Java
  • Swift 3 - 在视图控制器之间传递数据,然后传递到另一个视图控制器

    我正在尝试执行一个segue 但它不起作用 我想做的是发送我的视图控制器 主 中文本字段中的数据 之后我想将其发送到名为 OperationsController 的 ViewController 然后将其发送到另一个视图 CreateCo
  • 用向量 R 中的每个其他值减去向量中的每个值

    假设我有一个矩阵 data lt matrix seq 1 6 ncol 2 nrow 3 byrow TRUE A B 1 1 2 2 3 4 3 5 6 从 A 列开始 我想创建 A 中每个数字与 A 中每个其他数字之间的成对差异向量
  • 使 Windows Mobile 设备模拟蓝牙 HID 设备

    我正在寻找一种通过蓝牙将 Windows Mobile 设备连接到 PC 并使其作为 HID 设备 即键盘或鼠标 显示在 PC 上的方法 我想这主要是修改 Windows Mobile 设备上可用的蓝牙配置文件的问题 以便它公开蓝牙 HID
  • C# 中检测系统从睡眠状态唤醒的事件

    我需要检测系统电源状态模式 准确地说 我需要一个当 Windows 7 从睡眠状态唤醒时触发的事件 我已经在使用 SystemEvents PowerModeChanged SystemEvents PowerModeChanged 但此事
  • FFMpeg 复制流而不转码

    我正在尝试将多个文件中的所有流复制到一个文件中 而不对流进行转码 你经常做的事情ffmpeg效用由ffmpeg i file with audio mp4 i file with video mp4 c copy shortest file
  • 从应用程序播放音频,即使它在后台

    我很难确定如何在 iOS 应用程序的后台播放音频 我有一个 NSTimer 希望在达到 5 分钟时播放声音 即使在后台也是如此 我已经启用了音频背景模式 但不确定如何实现我的想法 在功能下选择以下选项 在您的 plist 中 确保 应用程序
  • 打印传递给函数的变量名称

    在某些情况下 我想打印调试样式的输出 如下所示 module test py def f a 5 b 8 debug a b line 18 我想要debug函数打印以下内容 debug info at test py 18 functio
  • 使用 jQuery“取消嵌套”嵌套块引用(以适应 tumblr 2015 年 9 月更新)

    我正在为一个转发量很大的博客开发一个新的 tumblr 主题 根据 tumblr 对评论链的最新更新 有些帖子变得非常冗长 虽然它在我的仪表板上看起来不错 但由于嵌套的块引用 它对我的 博客造成了严重破坏 我想模仿评论在我的仪表板上的外观
  • 如何对连续范围进行分组

    我知道一些基本的 SQL 但这个超出了我的能力范围 我看了高低 但没有骰子 我需要查看以下数据 我可以在应用程序层代码中执行此操作 但不幸的是 对于这一特定的情况 代码必须放在数据层中 我正在使用 T SQL Table Date Crew
  • 为 Android 构建 FFMPEG 3.1.4

    I used this and this构建最新版本的方法 3 1 4 FFMPEG安卓版本 构建成功 我有几个文件夹sources headers o files 但遗憾的是没有 so我需要的文件 是否有一些更新的脚本用于构建最新的FFM
  • Selenium 中的 SearchContext 和 WebDriver 接口有什么区别或者它们之间有什么关系?

    我在某个地方看到我们可以同时使用 WebDriver driver new FirefoxDriver or SearchContext driver new FirefoxDriver 我很困惑这两个不同的接口有什么区别 搜索上下文 搜索
  • 如何在 matplotlib 中并排绘制堆叠直方图?

    我希望在 matplotlib 中绘制两个并排堆叠的直方图 类似于下面的示例图像 我尝试过几种变体 bins np arange 10 a1 b1 c1 plt hist arr1 arr2 arr3 bins stacked True a
  • JavaScript if "x = (a || b || c)" 语句不起作用

    我正在用 javascript 编写一个简单的三角程序 我的 if 和 while 语句无法正常工作 因为它们只有在第一个条件为真时才会通过 即如果您输入正弦 它将起作用 但如果您输入余弦或正切 则不会
  • 使用 FIRE_TRIGGERS 进行批量插入不执行触发器

    我使用以下代码批量插入 CSV 文件 BULK INSERT CustomSelection FROM c asd a1 csv WITH FIRSTROW 2 FIELDTERMINATOR ROWTERMINATOR n FIRE TR
  • 节点中的 JavaScript 依赖注入和 DIP:require 与构造函数注入

    我是来自 NET 世界的 NodeJs 开发新手 我正在网上搜索在 Javascript 中重新分级 DI DIP 的最佳实践 在 NET 中 我会在构造函数中声明我的依赖项 而在 javascript 中 我看到一个常见的模式是通过 re