2

我正在重构我们的应用程序以包含依赖注入(通过构造函数注入)并且遇到了一个棘手的极端情况:

我们目前有一些ImageViewer对象,在实例化时会在程序集中搜索ImageViewerPlugin(抽象基类)实例,并使用反射实例化它们。这是在ImageViewer使用方法的构造函数中完成的(在所有具体插件类型的循环中调用),类似于:

private ImageViewerPlugin LoadPlugin(Type concretePluginType)
{
  var pluginConstructor = concretePluginType.GetConstructor(
    BindingFlags.Instance | BindingFlags.DeclaredOnly | BindingFlags.Public,
    null,
    new[] { typeof(ImageViewer) },
    null);

  return (ImageViewerPlugin) pluginConstructor.Invoke(
    new object[] { constructorParameter });
}

ImageViewerPlugin 类大致如下所示:

internal ImageViewerPlugin
{
  protected ImageViewer _viewer;
  protected ImageViewerPlugin(ImageViewer viewer)
  {
    _viewer = viewer;
  }
}

一个具体的实现大致是这样的:

internal AnImageViewerPlugin
{
  public AnImageViewerPlugin(ImageViewer viewer) : base(viewer)
  {
  }
}

每个ImageViewer实例都有自己的ImageViewerPlugin实例集合。

现在应用程序被重构为使用 DI 容器和构造函数注入,我发现这些插件具有需要由 DI 容器解决的依赖项(以前通过使用全局静态类隐藏),但我不知道如何在不使用服务定位器(反模式)的情况下做到这一点。

最明智的解决方案似乎是使用 DI 创建这些插件实例。这将允许我添加额外的构造函数参数,以通过构造函数注入注入它们所依赖的依赖项。但是如果我这样做,我如何viewer在注入其余参数值的同时传递特定的参数值?

我认为 anImageViewerPluginFactory将有助于实现这一点,但看不到如何实现这样的工厂,因为每个插件都可能具有不同的构造函数签名。

我该如何解决这种情况?还是我以完全错误的方式接近这个?

4

1 回答 1

2

因此,您有一个ImageViewer依赖于实例集合的ImageViewerPlugin实例,每个实例都依赖于 nImageViewer依赖于实例集合ImageViewerPlugin,每个实例都依赖于 n依赖于实例ImageViewer集合ImageViewerPlugin,每个实例都依赖于...图片 :-)

这是一个循环引用。将整个 DI 容器放在一边,手动执行此操作时将如何创建此层次结构?使用构造函数注入你不能。从下面的例子可以看出:

var plugin = new ImageViewerPlugin( [what goes here?] );
var viewer = new ImageViewer(plugin);

您将不得不以某种方式打破这个依赖循环,一般建议是在这种情况下使用属性注入:

var plugin = new ImageViewerPlugin();
var viewer = new ImageViewer(plugin);

// Property injection
plugin.Viewer = viewer;

但更重要的是,您应该仔细查看应用程序的设计,因为循环引用通常表明设计存在问题。例如,仔细看看插件需要查看器的哪些行为以及查看器需要插件的哪些行为。您也许可以将其提取到另一个类,如下所示:

var imageServices = new ImageServices();
var plugin = new ImageViewerPlugin(imageServices);
var viewer = new ImageViewer(imageServices, plugin);

这样就彻底解决了问题,但是这是否可行还要看你的情况。

使用最后一个解决方案,使用 Simple Injector 注册将相当简单。使用属性注入打破依赖关系时,可以使用RegisterInitializer(Action)方法。它允许您打破依赖循环。例如:

container.RegisterInitializer<ImageViewer>(viewer =>
{
    foreach (var plugin in viewer.Plugins)
    {
        plugin.Viewer = viewer;
    }
});
于 2013-03-15T00:38:15.550 回答