1

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

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

我正在使用 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>();

但是没有骰子...

那么,有人知道我如何让它工作吗?

4

1 回答 1

1

每当您绞尽脑汁想弄清楚如何在运行时创建实例时,您需要退后一步,寻找适合该问题的设计模式。DI 用于组合应用程序,但控制运行时行为应该是应用程序设计的一部分——即在应用程序组合运行的部分。

在这种特殊情况下,抽象工厂将是一个很好的选择。它允许您将组合服务(通过构造函数注入的服务)与运行时服务(作为方法参数传递的服务)分开。

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

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);
        }
    }
}

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

您也可以考虑使用适配器模式来为 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)
    {
    }
}
于 2015-02-24T20:05:53.957 回答