Я имел горячий спор с одним из моих коллег на внедрении зависимости и понял, что не вполне знал всех фактов на предмете.
Так, возьмите этот код (именно так, Вы знаете, мы используем замок Windsor),
IPlayerService service = Container.Resolve<IPlayerService>();
Вышеупомянутый код является, очевидно, примером DI с помощью МОК.
Однако см. код ниже (ОБНОВЛЕНИЕ: Предположите, что я передаю ВСЕ внешние зависимости через конструктора):
var playerClient = new PlayerClient();
var playerSkinClient = new PlayerSkinClient();
IPlayerService service = new PlayerService(playerClient, playerSkinClient);
Моя конкуренция была то, что вышеупомянутый код был примером шаблона DI, и что DI может существовать без МОК.
Теперь, мой коллега не полностью не соглашался с моей точкой, но он сказал, что вышеупомянутый код не является примером никакого шаблона, включающего DI.
Так, DI может использоваться как просто шаблон без какого-либо дополнительного frameworking?
Если так, является вышеупомянутый код примером его?
Наконец, что определяет шаблон DI, если он существует без понятия Контейнера.
Я буду проходить ответы, и комментарии более тщательно позже сегодня вечером, но просто требуемый для благодарности всех за хорошо продумали ответы и комментарии до сих пор!
В мире Java внедрение зависимостей часто включает в себя фреймворк, контейнер и так далее. Но нет необходимости во всей этой машинерии.
Суть инъекции зависимостей заключается в том, что класс может быть назначен объектам, от которых он зависит, вместо того, чтобы жестко кодировать их в реализации. Если вы можете "внедрить" эти "зависимости" извне класса, то у вас есть инъекция зависимостей. Фреймворк может быть хорошим способом сделать это, но он не обязателен.
В мире Python нет культуры фреймворков для инъекции зависимостей, поэтому многие программисты недоумевают, к чему вся эта суета. Для них DI - это "просто" возможность передавать объекты или классы в конструкторы.
Чтобы ответить на ваш конкретный вопрос:
Да, DI может быть реализовано без фреймворка.
Да, приведенный выше код является примером этого: PlayerService не знает, откуда взялись PlayerClient или PlayerSkinClient. Они были инжектированы в класс. Обратите внимание, что другие здесь ответили на этот вопрос "Нет".
См. выше.
Это действительно не пример DI.
Причина в том, что вы упорядочиваете зависимости ( PlayerClient
и PlayerSkinClient
в вашем случае) вместо того, чтобы упорядочивать зависимости для вас (передаваемые контейнером при создании бетонного объекта). IPlayerService
). Это «инверсия управления» - у вас вызывается конструктор, вместо того, чтобы вызывать его. Это основная причина использования шаблона DI, и наличие контейнера, который определяет, разрешает и помещает зависимости в объекты, которые их запрашивают, является конкретным способом сделать это.
Когда вы позволяете контейнеру управлять передачей зависимостей, вам не нужно писать и поддерживать код, который передает зависимости конкретному IPlayerService
. Это позволяет вам выполнять другие реализации PlayerClient
и PlayerSkinClient
(например, когда вы хотите имитировать их для построения тестов) и не обновлять код, который инициализирует IPlayerService
. Вот почему полезен шаблон DI.
Итак, можно ли использовать DI как шаблон без каких-либо дополнительных рамок?
Да, безусловно.
Если да, то приведенный выше код является примером этого?
Да, безусловно.
И наконец, что определяет шаблон DI, если он существует, без понятия Контейнера.
Объекту дано все, от чего он зависит; ему вводятся его зависимости.
Вот некоторые другие статьи, которые могут привести некоторые другие важные детали по этому вопросу в порядке «в первую очередь следует прочитать»:
Прочитав их, я обнаружил, что лучше понимаю предмет.
По словам Мартина Фаулера, который в некоторой степени является экспертом в этой области, внедрение зависимостей - это другое название конкретной реализации абстрактной концепции, известной как инверсия управления ( полная статья ).
В своей основе все средства 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, если у вас нет явного связующего / ассемблера / что-то, что связывает внедренные типы с конкретными реализациями, потому что класс, получающий зависимости, не знает разницы. Независимо от того, упорядочиваете ли вы зависимости во внешнем классе или во внешнем файле конфигурации, это детали реализации.
Прочтите это: http://dotnetslackers.com/articles/designpatterns/InversionOfControlAndDependencyInjectionWithCastleWindsorContainerPart1.aspx
Да, хотя по сравнению с чем-то вроде PicoContainer - или другими действительно крошечными контейнерами, все из которых протестированы и хорошо продуманы - в конечном итоге вы реализуете тот же набор функций. Лучшим примером этого является статья Ayende "Building an IoC container in 15 lines".
Пересмотрев свой ответ, я решил ответить "да". Я думаю, что основной когнитивный диссонанс здесь заключается в том, что из-за того, что разработчики путают тот факт, что многие/большинство DI/IoC-фреймворков предлагают аккуратные способы построения объектов, которые не выглядят как new
, именно это заставляет их придерживаться идей IoC.
Как уже упоминалось, определение Мартина Фаулера является каноническим, и оно довольно хорошо освещено в других источниках.
Забавно то, что первый пример - это вообще не DI, а пример анти-паттерна Service Locator - ничего не вводится .
Ваш собственный пример - это чистый DI, а точнее реализация паттерна Constructor Injection .
DI - это не один шаблон, а набор шаблонов и принципов. Когда мы подключаем все зависимости вручную, мы обычно называем это Poor Man's DI . Другими словами, контейнеры DI не являются обязательными, но настоятельно рекомендуется:)
См. Также это описание того, что контейнер DI привносит в таблицу .