IoC со статическими и динамическими зависимостями - PullRequest
4 голосов
/ 13 октября 2011

Я пытаюсь реализовать IoC в своем приложении.У меня есть эта модель:

interface IService;
interface IComponent;

class Service : IService
    Service()

class Component : IComponent
    Component(IService service, object runtimeValue) { }

В какой-то момент в моем приложении мне нужно получить IComponent.Мое приложение использует контейнер IoC (Unity).Я могу зарегистрировать Service в контейнере, но я не могу сделать то же самое для Component b / c его зависимости runtimeValue.В соответствии с this я должен использовать фабрику и вводить, что везде, где мне нужно получить IComponent:

interface IComponentFactory
     IComponent CreateComponent(object runtimeValue)

class ComponentProvider : IComponentProvider
     ComponentProvider(IComponentFactory factory) { }

     IComponent CreateAndCacheComponent(object runtimeValue) {
         _component = factory.CreateComponent(runtimeValue)
         return _component
     }

     // other methods

, я должен иметь возможность зарегистрировать фабрику в контейнере, поэтомуон должен иметь только статические зависимости.В то же время он должен иметь возможность предоставлять экземпляр службы типа IService, необходимый для создания компонента.
Вот фабричная реализация.Единственное, о чем я мог подумать, - это использовать делегат Func<> в качестве зависимости:

class ComponentFactory : IComponentFactory
    ComponentFactory(Func<IService> serviceFactoryDelegate)

    IComponent CreateComponent(object runtimeValue) {
        return new Component(serviceFactoryDelegate.Invoke(), runtimeValue)
    }

... и зарегистрировать делегат с контейнером как статическую фабрику, чтобы он вызывал контейнер для разрешенияслужба (я использую Unity 1.2 в .net 2.0):

Container
    .Configure<IStaticFactoryConfiguration>()
    .RegisterFactory<Func<IService>>(container => (Func<IService>)container.Resolve<IService>)

Теперь я могу использовать контейнер для разрешения ComponentProvider и получения компонента на основе значения времени выполнения:

// this happens inside CompositionRoot
provider = Container.Resovle<IComponentProvider>()
component = provider.CreateAndCacheComponent("the component")

Теперь у меня есть несколько вопросов по этому поводу:

  1. Я не рад, что фабрика звонит new Component(...).Разве это не DI этого бедняка?

  2. Сохраняется ли принцип Голливуда при использовании Func<IService> на конструкторе фабрики?Я имею в виду, что в конечном итоге он вызывает container.Resolve <> ... вроде SL.Единственное отличие состоит в том, что код находится в части регистрации контейнера приложения, а не внутри фабричного класса.

  3. Есть ли что-либо (еще) не так с этой реализацией, что касается DI иIoC обеспокоены?

Ответы [ 3 ]

1 голос
/ 13 октября 2011
  1. Нет, это не так. Целью фабрики является создание экземпляра конкретного класса.
  2. В принципе, да, но, как я уже спросил в своем комментарии, я не понимаю, почему это необходимо. Вы можете ввести экземпляр IService напрямую
  3. Это немного сложнее, чем нужно. Почему двойное перенаправление IComponentProvider -> IComponentFactory? Похоже, IComponentFactory не добавляет никакой пользы.

    Реализация ComponentProvider как это:

    class ComponentProvider : IComponentProvider
    {
        ComponentProvider(IService service) { _service = service; }
    
        IComponent CreateAndCacheComponent(object runtimeValue) {
            _component = new Component(_service, runtimeValue);
            return _component;
    }
    

    Это даст вам следующие преимущества:

    1. Вы избавляетесь от ненужного интерфейса IComponentFactory вместе с соответствующей реализацией.
    2. Нет необходимости регистрировать фабрику для IService

Вообще говоря, как вы это реализуете, зависит от того, что вам действительно нужно:

"runtimeValue" может быть одинаковым во время выполнения, например, Строка подключения, которая читается из настроек. В этом случае не будет необходимости в фабрике / провайдере, вы можете просто обновить экземпляр и зарегистрировать его в контейнере. Каждый, кто нуждается в IComponent, запрашивает один в конструкторе вместо провайдера.

Вы могли бы реализовать фабрику и передать ее как зависимость, если действительно "runtimeValue" между вызовами меняется на CreateAndCacheComponent.

1 голос
/ 14 октября 2011
  1. Это большой шаг от DI Бедного, но было бы неплохо, если бы вам не приходилось менять этот фабричный метод каждый раз, когда в конструктор Компонента добавляется новая зависимость.
  2. Это не проблема per se .Думайте об этом, как будто вы вводите анонимный фабричный класс.Его все еще можно смоделировать для модульного тестирования, а привязки можно изменить, так что вы по-прежнему получаете преимущества DI.Но это дополнительный уровень абстракции, который, вероятно, не является необходимым.В этом случае вы все еще можете избежать этого, введя IService непосредственно на фабрику, а не Func.
  3. . Обычно при использовании внедрения зависимостей вы хотите скорее ввести services чем значения .Тот факт, что вы обнаружите, что у вас есть оба, может указывать на то, что вам нужно пересмотреть API вашего класса.Например, может быть, вам следует передавать значение в методы класса, а не в конструктор .Трудно сказать, каким будет лучший подход, не зная больше деталей.
0 голосов
/ 13 октября 2011

К вопросу 1: нет ничего плохого в том, чтобы вызывать new на заводе. У вас есть единичный экземпляр в одном месте в вашем приложении; вы только что сделали это место вместо контейнера.

Если вам когда-либо понадобилось смоделировать или изменить реализации, вы бы просто смоделировали или изменили фабричную реализацию, а не один только Компонент.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...