我最近一直在使用 StructureMap,并且非常享受这种体验。然而,我可以看到人们如何很容易地把所有东西都接口化,并最终得到将大量接口纳入其构造函数的类。尽管当您使用依赖项注入框架时这确实不是一个大问题,但仍然感觉有些属性确实不需要仅仅为了连接它们而连接出来。

与仅向类添加属性相比,您如何界定接口输出的界限?

有帮助吗?

解决方案

想想你的设计。DI 允许您通过配置更改来更改代码的功能。它还允许您打破类之间的依赖关系,以便您可以更轻松地隔离和测试对象。您必须确定这在哪里有意义,在哪里没有意义。没有简单的答案。

一个好的经验法则是,如果它太难测试,那么您就会遇到一些单一职责和静态依赖项的问题。将执行单个函数的代码隔离到类中,并通过提取接口并使用 DI 框架在运行时注入正确的实例来打破静态依赖关系。通过这样做,您可以轻松地分别测试这两个部分。

其他提示

依赖注入的主要问题是,虽然它看起来是松散耦合的架构,但实际上并非如此。

您真正要做的是将这种耦合从编译时转移到运行时,但如果类 A 需要某个接口 B 才能工作,则仍然需要提供实现接口 B 的类的实例。

依赖注入应该仅用于应用程序中需要动态更改而无需重新编译基本代码的部分。

我认为对于控制反转模式有用的用途:

  • 插件架构。因此,通过制定正确的切入点,您可以定义必须提供的服务的合同。
  • 类似工作流的架构。您可以在其中连接多个组件,动态地将一个组件的输出连接到另一个组件的输入。
  • 每个客户端应用程序。假设您有不同的客户为您的项目的一组“功能”付费。通过使用依赖注入,您可以轻松地仅提供核心组件和一些“添加”组件,这些组件仅提供客户端已付费的功能。
  • 翻译。尽管这通常不是出于翻译目的,但您可以根据应用程序的需要“注入”不同的语言文件。其中包括根据需要的 RTL 或 LTR 用户界面。

依赖注入仅应用于需要动态更改的应用程序的部分而无需重新编译基本代码

DI 应该用于将代码与外部资源(数据库、Web 服务、xml 文件、插件架构)隔离。如果您正在测试依赖于数据库的组件,那么在代码中测试逻辑所需的时间对于许多公司来说几乎是令人望而却步的。

在大多数应用程序中,数据库不会动态更改(尽管可以),但一般来说,不将应用程序绑定到特定外部资源几乎总是好的做法。更改资源所涉及的数量应该很低(数据访问类在其方法中很少具有高于 1 的圈复杂度)。

“仅向类添加属性”是什么意思?

我的经验法则是使班级单元可测试。如果您的类依赖于另一个类的实现细节,则需要对其进行重构/抽象,以便可以单独测试这些类。

编辑:您在构造函数中提到了大量接口。我建议使用 setter/getter 代替。我发现从长远来看,它使事情更容易维护。

只有当它有助于分离关注点时我才会这样做。

就像跨项目一样,我会为我的一个库项目中的实现者提供一个接口,并且实现项目将注入他们想要的任何特定实现。

但仅此而已...所有其他情况只会使系统变得不必要的复杂

即使世界上所有的事实和过程..每个决定都归结为判断力 - 忘了我在哪里读到的
我认为这更像是一次体验/飞行时间通话。基本上,如果您将依赖项视为可能在不久的将来被替换的候选对象,请使用依赖项注入。如果我将“classA 及其依赖项”视为一个替换块,那么我可能不会将 DI 用于 A 的依赖项。

最大的好处是它将帮助您理解甚至揭示应用程序的架构。您将能够非常清楚地看到依赖链如何工作,并且能够对各个部分进行更改,而无需更改不相关的内容。您最终将得到一个松散耦合的应用程序。这将推动您进行更好的设计,并且当您能够不断改进时您会感到惊讶,因为您的设计将帮助您继续分离和组织代码。它还可以促进单元测试,因为您现在有一种自然的方式来替换特定接口的实现。

有些应用程序只是一次性的,但如果有疑问,我会继续创建界面。经过一些练习后,这并不是太大的负担。

我正在努力解决的另一个问题是 我应该在哪里使用依赖注入? 您对 StructureMap 的依赖在哪里?仅在启动应用程序中?这是否意味着所有的实现都必须从最顶层一直传递到最底层?

我使用 Castle Windsor/Microkernel,我没有其他任何经验,但我非常喜欢它。

至于你如何决定注射什么?到目前为止,以下经验法则对我很有帮助:如果类非常简单以至于不需要单元测试,您可以随意在类中实例化它,否则您可能希望通过构造函数获得依赖项。

至于你是否应该创建一个接口,而不是仅仅让你的方法和属性虚拟化,我认为你应该走接口路线,如果你a)可以看到该类在不同的应用程序中具有一定程度的可重用性(即a logger) 或 b) 如果由于构造函数参数的数量或构造函数中存在大量逻辑,则该类很难模拟。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top