Является ли Dependency Injection образцом и так ли это? - PullRequest
13 голосов
/ 30 июня 2010

У меня была горячая дискуссия с одним из моих коллег по внедрению зависимости, и я понял, что не совсем знаю все факты по этому вопросу.

Итак, возьмите этот код (просто вы знаете, мы используем Замок Виндзор)

IPlayerService service = Container.Resolve<IPlayerService>();

Приведенный выше код, очевидно, является примером DI, использующего IoC.

Однако, см. Код ниже ( ОБНОВЛЕНИЕ: Предположим, я передаю ВСЕ внешние зависимости через конструктор ):

var playerClient = new PlayerClient();
var playerSkinClient = new PlayerSkinClient();
IPlayerService service = new PlayerService(playerClient, playerSkinClient);

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

Теперь мой коллега не совсем не согласен с моей точкой зрения, но он сказал, что приведенный выше код не является примером какого-либо паттерна, включающего DI.

  1. Итак, можно ли использовать DI как шаблон без какого-либо дополнительного каркаса?

  2. Если это так, приведенный выше код является примером этого?

  3. Наконец, что определяет шаблон DI, если он существует, без понятия контейнера.

UPDATE

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

Ответы [ 8 ]

15 голосов
/ 30 июня 2010

По словам Мартина Фаулера, эксперта в данной области, Dependency Injection - это еще одно название для конкретной реализации абстрактной концепции, известной как Inversion of Control ( полная статья ).

В корне все средства DI: объекты, от которых зависит класс, чтобы они работали правильно, инициализируются и передаются в него извне, а не сам класс, отвечающий за получение / инициализацию этих объектов. Специфика того, как это достигается, выходит за рамки паттерна.

IoC - это то же самое, но, как говорит Мартин Фаулер, говорить, что «инверсия контроля» - это очень тупой способ описания происходящего.

Лично я не думаю, что "инъекция зависимости" намного лучше, хотя. Я бы описал это как «правильное использование конструкторов».

Пример не IoC / не DI:

class Foo
{
    void DoSomething()
    {
        DbConnection myConnection = new DbConnection(ConfigurationSettings...);
        myConnection.ExecuteCommand();
    }
}

То же самое, используя IoC / DI:

class Foo
{
    private DbConnection myConnection;

    public Foo(DbConnection conn)
    {
        myConnection = conn;
    }

    void DoSomething()
    {
        myConnection.ExecuteCommand();
    }
}

Я с уважением не согласен с теми, кто утверждает, что это не настоящий IoC / DI, если у вас нет явного связывателя / ассемблера / что-у-вас, которое связывает внедренные типы с конкретными реализациями, потому что класс, получающий зависимости, не не знаю разницу. Упорядочиваете ли вы зависимости во внешнем классе или во внешнем конфигурационном файле - это деталь реализации.

4 голосов
/ 30 июня 2010

Итак, можно ли использовать DI как шаблон без какого-либо дополнительного каркаса?

Да, абсолютно.

Если да, приведенный выше кодпример этого?

Да, абсолютно.

Наконец, что определяет шаблон DI, если он существует, без понятия контейнера.

Объекту дается все, от чего он зависит;его зависимость вводится в него.

3 голосов
/ 30 июня 2010

В мире Java внедрение зависимостей часто включает каркас, контейнер и т. Д.Но нет необходимости во всем этом механизме.

Суть внедрения зависимости заключается в том, что классу можно присваивать объекты, от которых он зависит, а не жестко кодировать их в реализации.Если вы можете «внедрить» эти «зависимости» извне класса, тогда у вас есть внедрение зависимостей.Фреймворк может быть хорошим способом сделать это, но он не обязателен.

В мире Python нет культуры фреймворков для внедрения зависимостей, поэтому многие программисты там задаются вопросом, о чем весь этот шум,Для них DI - это «просто» возможность передавать объекты или классы в конструкторы.

Чтобы ответить на ваш конкретный вопрос:

  1. Да, DI можно выполнить безframework.

  2. Да, приведенный выше код является примером этого: PlayerService не знает, откуда берется PlayerClient или PlayerSkinClient.Они были введены в класс.Обратите внимание, что другие здесь ответили на это «Нет».

  3. См. Выше.

1 голос
/ 30 июня 2010

Самое смешное, что первый пример - это вовсе не DI, а пример анти-паттерна Service Locator - ничего не вводится .

Ваш собственный пример - чистый DI - точнее, реализация шаблона Constructor Injection .

DI - это не один шаблон, а набор шаблонов и принципов.Когда мы вручную связываем все зависимости, мы обычно называем это DI для бедного человека.Другими словами, DI-контейнеры строго необязательны, но настоятельно рекомендуется:)

См. Также это описание того, что DI-контейнер приносит в таблицу .

1 голос
/ 30 июня 2010
  1. Да, хотя по сравнению с чем-то вроде PicoContainer - или другими действительно крошечными контейнерами, все из которых проверены и хорошо продуманы - вы собираетесь реализовать тот же набор функций в конец. Лучший пример этого - Ayende «Построение контейнера IoC из 15 строк» ​​.

  2. Пересматривая мой ответ, я собираюсь переключиться на да. Я думаю, что основной когнитивный диссонанс здесь заключается в том, что из-за того, что разработчики путают тот факт, что многие / большинство плат DI / IoC предлагают аккуратные способы конструирования объектов, которые не похожи на new, именно это заставляет их придерживаться идей IoC.

  3. Как уже упоминалось, Мартина Фаулера является каноническим определением и довольно хорошо освещено в других местах.

0 голосов
/ 30 июня 2010

Вот некоторые другие статьи, которые могут принести некоторые другие важные детали по теме, в порядке «следует читать первым»:

  1. Внедрение зависимостей Демистифицировано ;
  2. Зависимость инъекций .

Прочитав их, я понял, что лучше понимаю предмет.

0 голосов
/ 30 июня 2010

Это действительно не пример DI.

Причина в том, что вы упорядочиваете зависимости (PlayerClient и PlayerSkinClient в вашем случае) вместо того, чтобы упорядочивать зависимости для вас (передаваемые контейнером при создании бетона IPlayerService). Это «инверсия управления» - вместо вызова вы вызываете конструктор. Это основная причина использования шаблона DI, и наличие конкретного контейнера, который определяет, разрешает и помещает зависимости в объекты, которые их запрашивают, является конкретным способом сделать это.

Когда вы разрешаете контейнеру управлять передачей зависимостей, вам не нужно писать и поддерживать код, который передает зависимости конкретному IPlayerService. Это позволяет вам делать другие реализации PlayerClient и PlayerSkinClient (например, когда вы хотите имитировать их для построения тестов) и не нужно обновлять код, который инициализирует IPlayerService. Вот почему шаблон DI полезен.

0 голосов
/ 30 июня 2010
...