gpt4 book ai didi

dependency-injection - 依赖注入(inject)的可行替代方案?

转载 作者:行者123 更新时间:2023-12-03 08:03:45 25 4
gpt4 key购买 nike

我不喜欢基于构造函数的依赖注入(inject)。

我相信它会增加代码复杂性并降低可维护性,我想知道是否有任何可行的替代方案。

我不是在谈论将实现与接口(interface)分离的概念,以及从接口(interface)动态解析(递归)一组对象的方法。我完全支持这一点。然而,传统的基于构造函数的方法似乎存在一些问题。

1) 所有测试都依赖于构造函数。

去年在 MVC 3 C# 项目中广泛使用 DI 后,我发现我们的代码充满了这样的内容:

public interface IMyClass {
...
}

public class MyClass : IMyClass {

public MyClass(ILogService log, IDataService data, IBlahService blah) {
_log = log;
_blah = blah;
_data = data;
}

...
}

问题:如果我在实现中需要其他服务,我必须修改构造函数;这意味着这个类的所有单元测试都会中断。

即使与新功能无关的测试也需要至少重构以添加额外的参数并为该参数注入(inject)模拟。

这似乎是一个小问题,并且像 resharper 这样的自动化工具可以提供帮助,但是当像这样的简单更改导致 100 多个测试中断时,它肯定很烦人。实际上,我见过人们做愚蠢的事情来避免更改构造函数,而不是在发生这种情况时硬着头皮修复所有测试。

2) 服务实例被不必要地传递,增加了代码复杂性。
public class MyWorker {
public MyWorker(int x, IMyClass service, ILogService logs) {
...
}
}

仅当我在给定服务可用且已自动解析(例如 Controller )的上下文中,或者不幸的是,通过将服务实例传递到多个帮助程序类链中时,才可能创建此类的实例。

我一直看到这样的代码:
public class BlahHelper {

// Keep so we can create objects later
var _service = null;

public BlahHelper(IMyClass service) {
_service = service;
}

public void DoSomething() {
var worker = new SpecialPurposeWorker("flag", 100, service);
var status = worker.DoSomethingElse();
...

}
}

如果示例不清楚,我所说的是将解析的 DI 接口(interface)实例从多个层向下传递,除了在底层需要注入(inject)某些东西之外。

如果一个类不依赖于服务,那么它应该依赖于该服务。在我看来,这种认为存在类不使用服务而只是将其传递的“ transient ”依赖关系的想法是无稽之谈。

但是,我不知道更好的解决方案。

有没有什么东西可以提供 DI 的好处而没有这些问题?

我已经考虑在构造函数中使用 DI 框架,因为这解决了几个问题:
public MyClass() {
_log = Register.Get().Resolve<ILogService>();
_blah = Register.Get().Resolve<IBlahService>();
_data = Register.Get().Resolve<IDataService>();
}

这样做有什么缺点吗?

这意味着单元测试必须具有类的“先验知识”才能在测试初始化​​期间为正确的类型绑定(bind)模拟,但我看不到任何其他缺点。

注意。我的示例是用 c# 编写的,但我也偶然发现了其他语言中的相同问题,尤其是工具支持不太成熟的语言,这些都是令人头疼的问题。

最佳答案

在我看来,所有问题的根本原因在于没有正确执行 DI。使用构造函数 DI 的主要目的是清楚地说明某个类的所有依赖关系。如果某物依赖于某物,您总是有两种选择:使这种依赖显式化或将其隐藏在某种机制中(这种方式往往带来的麻烦多于利润)。

让我们看看你的陈述:

All tests depend on the constructors.

[snip]

Problem: If I need another service in my implementation I have to modify the constructor; and that means that all of the unit tests for this class break.



使一个类依赖于其他一些服务是一个相当大的变化。如果您有多个服务实现相同的功能,我认为存在设计问题。正确的模拟和制作测试满足 SRP(就单元测试而言,归结为“为每个测试用例编写单独的测试”)并且独立应该解决这个问题。

2) Service instances get passed around unnecessarily, increasing code complexity.



DI 最常见的用途之一是将对象创建与业务逻辑分开。在您的情况下,我们看到您真正需要的是创建一些 Worker,而后者又需要几个依赖项,这些依赖项通过整个对象图注入(inject)。解决此问题的最佳方法是永远不要做任何 new s 在业务逻辑中。对于这种情况,我更愿意注入(inject)一个 worker 工厂,从 worker 的实际创建中抽象出业务代码。

I've contemplated using the DI framework inside the constructors instead as this resolves a couple of the problems:

public MyClass() {
_log = Register.Get().Resolve<ILogService>();
_blah = Register.Get().Resolve<IBlahService>();
_data = Register.Get().Resolve<IDataService>();
}

Is there any downside to doing this?



作为一个好处,您将获得使用 Singleton 的所有缺点。模式(不可测试的代码和应用程​​序的巨大状态空间)。

所以我会说 DI 应该正确完成(就像任何其他工具一样)。您的问题 (IMO) 的解决方案在于了解 DI 和对团队成员的教育。

关于dependency-injection - 依赖注入(inject)的可行替代方案?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9155469/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com