结构图和未设置DI / IoC的对象

时间:2015-02-24 18:33:32

标签: c# dependency-injection inversion-of-control structuremap3

我有一种情况,我已经创建了一个工厂方法来创建一个对象。但是,该对象具有需要在创建对象之前执行的样板代码。修复这部分设计超出了这个问题的范围。

此外,创建对象时,屏幕上会更新状态显示。这要求在创建此对象之前,此状态显示在实例化之前可见,并且应用程序处于运行状态。它作为依赖项传递给工厂。

我正在使用StructureMap的v3.1.4.143。

所以,这就是我在正常世界(IoC前)所做的事情:

GraphicsInterface GetGraphics()
{
    VideoDevicesList.GetVideoDevices();

    // Some logic here to determine the device to use...
    // Also, a status display is being updated to inform the user of 
    // what's happening at this point.
    VideoDevice device = ...;

    // The second parameter is a constant value, but the first is not.
    return new GraphicsInterface(device, featureLevels.FL5);
}

看起来很简单,但理想情况下我希望能够通过注射传递图形对象,因为在许多地方都需要它。

所以,在结构图中,我创建了一个工厂函数来执行上述操作。然而它给了我悲伤。

new Container(obj =>
      {
          // This is passed to the object that depends on it.
          // I've just left it out for brevity.
          // It'd go something like:  _graphics = _getGraphicsFactory();
          // where _getGraphicsFactory is the factory function below.
          For<Func<IStatusDisplay, GraphicsInterface>>
             .Use<Func<IStatusDisplay, GraphicsInterface>>(GetGraphics);
      }

这只会给我一个关于未注册GraphicsInterface的错误。没关系,我应该能够注册GraphicsInterface对象。除了我不能注册GraphicsInterface,因为构造函数需要两个参数,其中一个必须在创建对象之前被查询,并且只能通过上面的GetVideoDevices方法设置并且看起来似乎是StructureMap尝试当我调用_getGraphicsFactory()时,为我创建对象(这很奇怪,我希望它能够执行我的函数来创建对象)。

我甚至尝试在我的GetVideoDevices方法中调用GetInstance:

_container
    .With<VideoDevice>(device)
    .With<FeatureLevel>(FeatureLevel.FL5)
    .GetInstance<Graphics>();

但没有骰子......

那么,有没有人知道我是如何让这个工作的?

1 个答案:

答案 0 :(得分:1)

每当你试图弄清楚如何在运行时创建实例时,你需要退后一步,寻找适合问题的设计模式。 DI用于组合应用程序,但控制运行时行为应该是应用程序设计的一部分 - 即在应用程序组成之后运行的部分。

在这种特殊情况下,Abstract Factory会很合适。它允许您将组合服务(通过构造函数注入的服务)与运行时服务(作为方法参数传递的服务)分开。

但是,您应该限制工厂完成一件事 - 创建运行时实例。所有其他工作应该是其他服务的一部分。这为您提供了一种将运行时对象注入服务的简洁方法,并且仍允许独立于此步骤测试服务行为。

public interface IGraphicsFactory
{
    GraphicsInterface Create(VideoDevice device);
    void Release(GraphicsInterface graphicsInterface);
}

public class GraphicsFactory : IGraphicsFactory
{
    private readonly FeatureLevel featureLevel;

    // Parameters injected are done so by the DI container
    public GraphicsFactory(FeatureLevel featureLevel)
    {
        this.featureLevel = featureLevel;
    }

    // Parameters passed are part of the application runtime state
    public GraphicsInterface Create(VideoDevice device)
    {
        return new GraphicsInterface(device, this.featureLevel);
    }

    // Method for releasing disposable dependencies (if any)
    public void Release(GraphicsInterface graphicsInterface)
    {
        var disposable = graphicsInterface as IDisposable;
        if (disposable != null)
            disposable.Dispose();
    }
}

然后可以在应用程序组合期间将工厂提供给服务,并且可以在运行时创建GraphicsInterface的运行时实例。根据您的要求,通过将其注入多个服务的构造函数,可以在多个位置轻松完成。

public class SomeService : ISomeService
{
    private readonly IGraphicsFactory graphicsFactory;

    public SomeService(IGraphicsFactory graphicsFactory)
    {
        if (graphicsFactory == null)
            throw new ArgumentNullException("graphicsFactory")

        this.graphicsFactory = graphicsFactory;
    }

    public void DoSomething()
    {
        // Get video device here. It will likely be best to 
        // delegate that to another specialized service
        // that is injected into this class.
        VideoDevice device = ...;

        var graphics = this.graphicsFactory.Create(device);
        try
        {
            // Do something with graphics
        }
        finally
        {
            this.graphicsFactory.Release(graphics);
        }
    }
}

至于选择要使用的设备,可以使用另一个抽象工厂完成,或者如果它经常完成,您可以使用Strategy Pattern在合成时加载所有选项,然后在运行时选择性地选择设备。或者,如果您的设备是一次性的,您可以制作抽象工厂策略或者寻找一些更先进的设计模式来清理它们。

您可能还会考虑使用适配器模式为GraphicsInterface创建一个抽象,如果它还没有一个可以注入(和交换)的合适的,它包含您所拥有的所有成员。

public interface IGraphicsInterfaceAdapter
{
    // Extract all public properties of GraphicsInteface and define them here.
}

public class GraphicsInterfaceAdapter : IGraphicsInterfaceAdapter
{
    public GraphicsInterfaceAdapter(VideoDevice device, FeatureLevel featureLevel)
        : base(device, featureLevel)
    {
    }
}