有什么理由不将我的IoC用作常规设置存储库?

时间:2020-03-06 14:32:29  来源:igfitidea点击:

假设ApplicationSettings类是适用于我的应用程序的通用设置存储库,例如TimeoutPeriod,DefaultUnitOfMeasure,HistoryWindowSize等。假设MyClass使用了其中一种设置DefaultUnitOfMeasure。

我阅读了正确使用控制容器反转的信息,如果我错了,请更正我,因为我们在其构造函数中定义了一个类的依赖项:

public class MyClass {
  public MyClass(IDataSource ds, UnitOfMeasure default_uom) {...}
}

然后调用实例化课程,例如

var mc = IoC.Container.Resolve<MyClass>();

在其中为IDataSource分配了一个具体的实现,并已连接default_uom以便从ApplicationSettings.DefaultUnitOfMeasure属性进行实例化。但是,我想知道,是否所有这些箍圈真的是必需的。我应该为自己做些什么麻烦

public class MyClass {
  public MyClass(IDataSource ds) {
    UnitOfMeasure duom = IoC.Container.Resolve<UnitOfMeasure>("default_uom");
  }
}

是的,我的许多类最终都依赖于IoC.Container,但这仍然是我大多数类都会拥有的依赖。只要类是对的,我似乎就应该充分利用它。请敏捷专家,告诉我我错了。

解决方案

Yes, many of my classes end up with a dependency on IoC.Container but that is a dependency that most of my classes will have anyways.

我认为这是问题的症结所在。如果实际上大多数类都耦合到了IoC容器本身,那么我们就需要重新考虑设计。

一般来说,在引导过程中,应用应仅直接引用一次容器类。将第一个钩子放入容器后,对象图的其余部分应完全由容器管理,并且所有这些对象都应忽略它们是由IoC容器创建的事实。

根据我的IoC容器,我通常没有很多类。我通常尝试将IoC内容包装在我注入到其他类中的Facade对象中,但是通常我的IoC注入大部分仅在应用程序的较高层中完成。

如果我们以自己的方式进行操作,则无法在不为测试创建IoC配置的情况下测试MyClass。这会使测试难以维护。

另一个问题是,我们将需要软件的超级用户来更改配置,以编辑IoC配置文件。这是我要避免的事情。我们可以将IoC配置拆分为普通配置文件和IoC特定内容。但是,我们也可以使用常规的.Net config功能来读取配置。

IoC.Container.Resolve("default_uom");

我将其视为一种经典的反模式,在这种情况下,我们将IoC容器用作服务定位器,导致的主要问题是:

  • 如果容器配置不正确,应用程序将不再快速失败(我们只会在它第一次尝试解析代码中的特定服务时才知道它,除非特定的一组逻辑/情况,否则可能不会发生)。
  • 难以测试-当然并非不可能,但是我们必须为测试创建一个windsor容器的真实(半配置)实例,或者使用IWindsorContainer的模拟注入单例-这给测试增加了很多麻烦,与仅能够通过构造函数/属性将模拟/存根服务直接传递到要测试的类相比。
  • 难以维护此类应用程序(配置未集中在一个位置)
  • 违反了许多其他软件开发原则(DRY,SOC等)

原始声明中最重要的部分是,如果大多数类通过构造函数/依赖项注入了所有服务,那么大多数类将依赖于IoC单例,那么与IoC的紧密耦合应该是IoC的例外。规则通常,我唯一依赖容器的时间是在做一些棘手的事情时,例如,尝试避免循环依赖问题,或者出于某种原因希望在运行时创建组件,即使这样,我也经常可以避免除了通用的IServiceProvider接口之外,它还依赖于其他任何东西,如果我需要在原始项目之外的环境中重用组件,则可以交换家用IoC或者服务定位器实现。

要评论特定示例:

public class MyClass {
    public MyClass(IDataSource ds) {
        UnitOfMeasure duom = IoC.Container.Resolve<UnitOfMeasure>("default_uom");
    }
}

这使得重用课程变得更加困难。更具体地说,它使我们难以在要使用的狭窄使用模式之外实例化类。这将表现出来的最常见的地方之一就是在尝试测试课程时。如果可以将UnitOfMeasure直接传递给构造函数,则测试该类要容易得多。

同样,我们为UOM实例选择的名称(" default_uom")意味着可以根据类的用法来覆盖该值。在这种情况下,我们将不需要像这样在构造函数中"硬编码"该值。

使用构造函数注入模式不会使类依赖于IoC,相反,它使客户端可以选择是否使用IoC。