0

У меня возникла ситуация, когда я создал фабричный метод для создания объекта. Тем не менее, у объекта есть шаблонный код, который требует выполнения до создания объекта. Фиксация этой части дизайна выходит за рамки этого вопроса.StructureMap и объекты, не настроенные для DI/IoC

Также, когда объект создан, на экране обновляется отображение состояния. Для этого требуется, чтобы этот индикатор состояния был создан раньше и был видимым, и приложение должно находиться в рабочем состоянии перед созданием этого объекта. Он передается фабрике в качестве зависимости.

Я использую v3.1.4.143 из StructureMap.

Итак, вот что я буду делать в нормальном мире (до 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() (что странно, я бы ожидал, что он выполнит мою функцию для создания объекта).

Я попытался даже называя GetInstance как это внутри моего метода GetVideoDevices:

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

Но не кости ...

Итак, кто-нибудь есть идеи о том, как бы я получить эту работу ?

ответ

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

Что касается выбора устройства для использования, что может быть сделано или с другим Abstract Factory, или, если это то, что делается часто, вы могли бы использовать 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) 
    { 
    } 
} 
+0

Итак, я создал абстрактную фабрику и создал специализированный сервис, который дает мне видеоустройство, которое я хочу. Это все работает. Однако, когда я ввожу свой экземпляр ISplash на фабрику, это совершенно другой экземпляр, чем тот, который был передан моему контексту приложения. Я думал, что StructureMap будет передавать один и тот же экземпляр конструкторам. Наверное, я ошибся? И если да, то каким образом я могу передать только один экземпляр? (Я не уверен, что мне нужны синглтоны здесь, но если это то, что я должен делать ...) – Mike

+0

Также я узнал, что если я использую ForConcreteType, он передает один и тот же экземпляр всем конструкторам. Но мне это кажется странным. – Mike

+0

И неважно ... конечно, я был глупым и неправильно определил мои конструкторы (используя конкретные типы вместо интерфейсов). Теперь поведение точно так, как я ожидал. – Mike

Смежные вопросы