OOD с использованием контейнеров IoC - как построить зависимые объекты? - PullRequest
5 голосов
/ 13 августа 2011

Я пытаюсь стать лучше с IoC, DI и OOD для лучшей тестируемости и более слабой связи.

Поэтому, когда мы разрабатываем классы с интенсивным использованием IoC и DI, мы можем в конечном итоге получить классы с несколькими зависимостями, например

class Processor
{
    private IService1 m_Service1;
    private IService2 m_Service2;
    private IService3 m_Service3;

    //approach 1 
    public Processor(IService1 service1, IService2 service2, IService3 service3)
    {
        m_Service1 = service1;
        m_Service2 = service2;
        m_Service3 = service3;
    }
    //approach 2
    public Processor(IContainer container)
    {
        m_Service1 = container.Resolve<IService1>();
        m_Service2 = container.Resolve<IService2>();
        m_Service3 = container.Resolve<IService3>();
    }

    //approach 3
    public delegate Processor Factory();

}

Я думаю, что здесь должен быть обычный подход. Мы можем оставить конструктор с 3 параметрами, но если мы создаем приложение, используя autofac (например), скорее всего, оно будет редко использоваться, кроме как путем разрешения типов из некоторого экземпляра контейнера, такого как

    Processor proc = new Processor(
 container.Resolve<IService1>(),
 container.Resolve<IService2>(),
 container.Resolve<IService3>());

поэтому я думаю, что, возможно, подход 2 лучше, когда мы зависим от нескольких типов из контейнера. В любом случае нам нужно будет где-нибудь добавить ссылку на autofac, поэтому есть ли причины не делать это сейчас?

Autofac также предоставляет метод фабрики делегатов

http://code.google.com/p/autofac/wiki/DelegateFactories

var processorFactory = container.Resolve<Processor.Factory>();
Processor processor = processorFactory.Invoke();

Таким образом, у нас также есть подход 3 - мы не будем использовать конструкторы для создания экземпляров нашего класса, вместо этого мы будем вызывать разрешенный делегат из контейнера, и это разрешит нам зависимости.

Поскольку я довольно новичок в IoC, трудно сказать, когда нам следует использовать 1,2,3. У них есть свои преимущества и недостатки.

Я думаю, что в общем случае, если у класса есть 1 зависимость, мы, вероятно, всегда можем использовать подход 1 ... кроме этого, я действительно не уверен, что выбрать и когда.

ОБНОВЛЕНИЕ Я читал об анти-паттерне сервисного локатора, но у меня есть 4-й (или настоящий 3-й подход)

он близок к ServiceLocator, за исключением его, мы передаем объект, который выглядит следующим образом

public class ServiceLocatorObject
{
        private IService1 m_Service1;
        private IService2 m_Service2;
        private IService3 m_Service3;
        public IService1 Service1 {get {return m_Service1;}}
        public IService2 Service2 {get {return m_Service2;}}
        public IService3 Service3 {get {return m_Service3;}}

        public ServiceLocatorObject(IService1 service1, IService2 service2, IService3 service3)
        {
            m_Service1 = service1;
            m_Service2 = service2;
            m_Service3 = service3;
        }
}

А теперь мы создаем

//approach 4
public Processor(ServiceLocatorObject servicesToUse)
{
    m_Services = servicesToUse;
}

Теперь мы отсоединили наш класс от реализаций сервисов, и стало ясно, какие реальные зависимости ему нужны (если мы предположим, что требуются все сервисы, доступные для переданного объекта), потому что мы не передаем контейнер, который может содержать 100 реализаций. И этот объект может даже использоваться повторно, если в нашем приложении может потребоваться эта комбинация из трех сервисов. Поэтому мы используем конструктор DI, а не шаблон ServiceLocator. интерфейс понятен и не перегружен зависимостями, новый класс может быть хорошим кандидатом для повторного использования.

Что бы вы сказали об этом?

Ответы [ 3 ]

6 голосов
/ 13 августа 2011

Шаблон расположения службы в наши дни часто считается анти-шаблоном (с использованием container.Resolve и внедрением контейнера).

После того, как МНОГО боролся с этой концепцией сам и пытался решить, нравится мне это или нет, я пришел к личному осознанию того, что я согласен с тем, что местоположение службы - это анти-паттерн - потому что он скрывает взаимозависимости, которые существуют и которые являются основной концепцией ООП.

Прочитайте здесь: http://blog.ploeh.dk/2010/02/03/ServiceLocatorIsAnAntiPattern.aspx

Мне действительно нравится тот факт, что в варианте 1 Process CLEARLY выражает свою зависимость от каждой из перечисленных служб, которые являются параметрами в конструкторе. Это делает зависимости очень очевидными .... и, кроме того, я думаю, что это помогает продвигать хороший дизайн.

Просто сказать, что Processor принимает IContainer, не очень много говорит нам ... и, следовательно, вам нужно внимательнее присмотреться к выявлению взаимозависимостей.

6 голосов
/ 13 августа 2011

Ответ, данный JeffN825, является правильным, но я хотел бы добавить к этому, что вы никогда не создадите новый экземпляр Processor, используя такой контейнер:

Processor proc = new Processor(
    container.Resolve<IService1>(),
    container.Resolve<IService2>(),
    container.Resolve<IService3>());

Скорее, вы позволите контейнеру автоматически подключить зависимости и разрешить его за один раз :

Processor proc = container.Resolve<Processor>();
0 голосов
/ 13 августа 2011

Речь идет не о количестве зависимостей и не о решениях для каждого класса. Подход 2 вводит новую зависимость, но если вы хотите полагаться на контейнер IoC, то это хороший подход. Подход 3 похож на второй, но давайте сделаем кое-что на Фабрике в будущем. Подход 1 является самым простым, не опирается ни на что и должен использоваться для зависимостей, которыми вы обычно не управляете через контейнер IoC.

...