Должна ли инъекция зависимостей происходить за счет инкапсуляции?


Если я правильно понимаю, типичный механизм для инъекции зависимостей-это инъекция либо через конструктор класса, либо через публичное свойство (член) класса.

Это раскрывает вводимую зависимость и нарушает принцип инкапсуляции ООП.

правильно ли я определяю этот компромисс? Как вы справляетесь с этой проблемой?

пожалуйста, Также см. Мой ответ на мой собственный вопрос ниже.

21 115

21 ответ:

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

когда мы используем инъекцию IoC/dependency, мы не используем концепции ООП. По общему признанию, мы используем язык OO в качестве "хоста", но идеи, лежащие в основе IoC, исходят из компонентно-ориентированной разработки программного обеспечения, а не OO.

компонентное программное обеспечение - это все об управлении зависимостями-примером общего использования является механизм сборки .NET. Каждая сборка публикует список сборок, которые она ссылки, и это делает его гораздо проще собрать (и проверить) части, необходимые для запущенного приложения.

применяя аналогичные методы в наших программах OO через IoC, мы стремимся упростить настройку и обслуживание программ. Публикация зависимостей (как параметров конструктора или что-то еще) является ключевой частью этого. Инкапсуляция на самом деле не применяется, так как в мире, ориентированном на компоненты/службы, нет "типа реализации" для утечки сведений от.

к сожалению, наши языки в настоящее время не отделяют мелкозернистые, объектно-ориентированные понятия от более грубозернистых компонентно-ориентированных, так что это различие, которое вы должны держать в своем уме только :)

это хороший вопрос - но в какой-то момент, инкапсуляция в чистом виде должен нарушается, если объект когда-либо выполнит свою зависимость. Какой-то провайдер зависимости должны знайте, что объект, о котором идет речь, требует Foo, и поставщик должен иметь возможность предоставления Foo на объект.

классически этот последний случай обрабатывается, как вы говорите, через аргументы конструктора или методы сеттера. Однако, это не обязательно верно - я знаю, что последние версии Spring DI framework в Java, например, позволяют аннотировать частные поля (например, с @Autowired) и зависимость будет установлена через отражение без необходимости выставлять зависимость через любой из классов публичных методов / конструкторов. Это может быть такое решение, которое вы искали.

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

В идеале при работе с объектами, вы взаимодействуете с ними через интерфейс в любом случае, и чем больше вы это делаете (и имеете зависимости, подключенные через DI), тем меньше вам на самом деле приходится иметь дело с конструкторами самостоятельно. В идеальной ситуации ваш код не имеет дело с конкретными экземплярами классов или даже никогда не создает их; поэтому ему просто присваивается IFoo через DI, не беспокоясь о том, что конструктор FooImpl указывает, что он должен делать свою работу, а на самом деле даже не осознавая 'ы. С этой точки зрения, заключение идеальный.

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

это раскрывает вводимую зависимость и нарушает принцип инкапсуляции ООП.

Ну, честно говоря, все, что нарушает инкапсуляцию. :) Это своего рода нежный принцип, к которому нужно хорошо относиться.

Итак, что нарушает инкапсуляцию?

наследование тут.

" поскольку наследование предоставляет подкласс к деталям реализации его родителя, часто говорят, что 'наследование нарушает инкапсуляцию'". (Банда четырех 1995:19)

аспектно-ориентированное программированиетут. Например, вы регистрируете обратный вызов onMethodCall (), и это дает вам отличную возможность вводить код в обычную оценку метода, добавляя странные побочные эффекты и т. д.

объявление друга в C++ тут.

расширение класса в Ruby тут. Просто переопределите строку метод где-то после того, как строковый класс был полностью определен.

Ну, много чего тут.

инкапсуляция является хорошим и важным принципом. Но не единственный.

switch (principle)
{
      case encapsulation:
           if (there_is_a_reason)
      break!
}

да, DI нарушает инкапсуляцию (также известную как "сокрытие информации").

но реальная проблема возникает, когда разработчики используют его в качестве предлога для нарушения принципов KISS (держите его коротким и простым) и YAGNI (вам это не понадобится).

лично я предпочитаю простые и эффективные решения. Я в основном использую оператор " new " для создания экземпляров зависимостей с сохранением состояния, когда и где они необходимы. Он прост, хорошо инкапсулирован, прост для понимания и прост в использовании тест. Так почему бы и нет?

Это не нарушает инкапсуляцию. Вы предоставляете сотрудника, но класс должен решить, как он используется. Пока вы следуете скажи Не спрашивай все нормально. Я нахожу инъекцию конструктора предпочтительной, но сеттеры могут быть прекрасными, а также до тех пор, пока они умны. То есть они содержат логику для поддержания инвариантов класса представляет.

хороший контейнер/система впрыски depenancy прибавлять на впрыска конструктора. Зависимые объекты будут инкапсулированы и не должны быть выставлены публично вообще. Кроме того, используя систему DP, ни один из ваших кодов даже не "знает" детали того, как создается объект, возможно, даже включая создаваемый объект. В этом случае есть больше инкапсуляции, так как почти весь ваш код не только защищен от знания инкапсулированных объектов, но даже не имеет участие в строительстве объектов.

теперь я предполагаю, что вы сравниваете случай, когда созданный объект создает свои собственные инкапсулированные объекты, скорее всего, в своем конструкторе. Мое понимание DP заключается в том, что мы хотим снять эту ответственность с объекта и передать ее кому-то другому. С этой целью "кто-то другой", который является контейнером DP в этом случае, имеет интимное знание, которое "нарушает" инкапсуляцию; преимущество в том, что он тянет это знание вне объекта, само по себе. Кто-то должен иметь его. Остальные приложения не.

Я бы подумал об этом так: контейнер/система инъекции зависимости нарушает инкапсуляцию, но ваш код этого не делает. На самом деле, ваш код более "инкапсулирован", чем когда-либо.

чистая инкапсуляция-это идеал, который никогда не может быть достигнут. Если бы все зависимости были скрыты, то у вас вообще не было бы необходимости в DI. Подумайте об этом таким образом, если у вас действительно есть частные значения, которые могут быть интернализованы внутри объекта, скажем, например, целочисленное значение скорости объекта car, то у вас нет внешней зависимости и нет необходимости инвертировать или вводить эту зависимость. Эти виды значений внутреннего состояния, которые управляются исключительно частными функциями, являются тем, что вы хотите инкапсулировать всегда.

но если вы строите автомобиль, который хочет определенный вид объекта двигателя, то у вас есть внешняя зависимость. Вы можете либо создать экземпляр этого движка - например, new GMOverHeadCamEngine () - внутри конструктора объекта car, сохраняя инкапсуляцию, но создавая гораздо более коварную связь с конкретным классом GMOverHeadCamEngine, либо вы можете ввести его, позволяя объекту Car работать агностически (и гораздо более надежно) например, интерфейс IEngine без конкретной зависимости. Независимо от того, используете ли вы контейнер IOC или простой DI для достижения этого, дело не в том, что у вас есть автомобиль, который может использовать многие виды двигателей, не будучи связанным ни с одним из них, что делает вашу кодовую базу более гибкой и менее подверженной побочным эффектам.

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

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

  • классический OO использует конструкторы для определения публичного контракта "инициализации" для потребителей класса (скрывая все детали реализации; aka инкапсуляция). Этот контракт может гарантировать, что после создания экземпляра у вас есть готовый к использованию объект (т. е. никаких дополнительных шагов инициализации, которые будут запомнены (er, забыты) пользователь.)

  • (конструктор) DI бесспорно нарушает инкапсуляцию путем кровотечения внедрение деталь через этот открытый интерфейс конструктора. Пока мы все еще рассматриваем публичный конструктор, ответственный за определение контракта инициализации для пользователей, мы создали ужасное нарушение инкапсуляции.

Теоретический Пример:

класс Фу имеет 4 метода и нуждается в целое число для инициализация, поэтому его конструктор выглядит как Foo (int size) и это сразу понятно пользователям класса Фу что они должны предоставить в размере при создании экземпляра для работы Foo.

скажем, эта конкретная реализация Foo также может нуждаться в IWidget делать свою работу. Инъекция конструктора этой зависимости заставила бы нас создать конструктор типа Foo (int size, IWidget виджет)

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

параметр size не является зависимостью - это просто значение инициализации для каждого экземпляра. МОК-это Денди для внешние зависимости (например, виджет), но не для инициализации внутреннего состояния.

еще хуже, что делать, если виджет необходим только для 2 из 4 методов этого класса; я могу нести накладные расходы на создание экземпляра для виджета, даже если он не может быть использован!

Как скомпрометировать / примирить это?

один из подходов заключается в переключении исключительно на интерфейсы для определения контракта операции; и отменить использование конструкторов пользователями. Чтобы быть последовательным, все объекты должны быть доступны только через интерфейсы и создаваться только через некоторую форму решателя (например, контейнер IOC/DI). Только контейнер получает инстанцировать вещи.

Это заботится о зависимости виджета, но как мы инициализируем "размер", не прибегая к отдельному методу инициализации на интерфейсе Foo? Используя это решение, мы потеряли возможность гарантировать, что экземпляр Foo будет полностью инициализирован к моменту получения экземпляра. Лентяй, потому что мне очень нравится идеи и простоту инъекции конструктор.

Как добиться гарантированной инициализации в этом мире DI, когда инициализация-это больше, чем только внешние зависимости?

как Джефф грудины указал в комментарии на вопрос, ответ полностью зависит от того, как ты определяешь инкапсуляция.

кажется, есть два основных лагеря того, что означает инкапсуляция:

  1. все, что связано с объектом-это метод объекта. Так,

Это зависит от того, действительно ли зависимость является детализацией реализации или чем-то, что клиент хотел бы/должен знать тем или иным образом. Одна вещь, которая имеет значение, - это то, на какой уровень абстракции нацелен класс. Вот несколько примеров:

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

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

для серой зоне, скажем, у вас есть класс, который делает некоторые моделирования Монте-Карло. Ему нужен источник случайности. Одной стороны, тот факт, что он нуждается в этом, является деталью реализации в том, что клиенту действительно все равно, откуда берется случайность. С другой стороны, поскольку реальные генераторы случайных чисел делают компромиссы между степенью случайности, скоростью и т. д. то, что клиент может захотеть контролировать, и клиент может захотеть контролировать посев, чтобы получить повторяемое поведение, инъекция может иметь смысл. В этом случае я бы предложил предложить способ создания класса без указания генератора случайных чисел, и использовать поток-локальный Синглтон по умолчанию. Если / когда возникает необходимость в более тонком управлении, предоставьте другой конструктор, который позволяет вводить источник случайности.

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

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

Но вернемся к кодированию. инкапсуляция "прячет детали реализации от чего-то, используя эту реализацию."Инкапсуляция-это хорошая вещь, потому что детали реализации могут меняться без ведома пользователя класса.

при использовании инъекции зависимостей, инъекция конструктора используется для построения сервис тип объектов (в отличие от объектов сущности/значения, которые моделируют состояние). Любые переменные-члены в типе сервиса объект представляет детали реализации,которые не должны просачиваться. например, номер порта сокета, учетные данные базы данных, другой класс для вызова для выполнения шифрования, кэш и т. д.

The конструктор имеет значение, когда класс изначально создавался. Это происходит на этапе строительства, когда ваш контейнер DI (или завод) соединяет все объекты обслуживания. Контейнер знает только о деталях реализации. Он знает все о детали внедрения как ребята на фабрике Kombi знают о свечах зажигания.

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

Это я везу своего Комби на пляж.

теперь вернемся к инкапсуляции. Если детали реализации изменяются, то класс, использующий эту реализацию во время выполнения, не нуждается изменить. Инкапсуляция не нарушается.

Я тоже могу поехать на своей новой машине на пляж. Инкапсуляция не нарушается.

Если детали реализации изменяются, контейнер DI (или завод) должен измениться. Вы никогда не пытались скрыть детали реализации от завода в первую очередь.

поборовшись с этим вопросом немного дальше, я теперь считаю, что инъекция зависимостей в какой-то степени нарушает инкапсуляцию. Не поймите меня неправильно, хотя - я думаю, что использование инъекции зависимости стоит компромисса в большинстве случаев.

причина, по которой DI нарушает инкапсуляцию, становится ясной, когда компонент, над которым вы работаете, должен быть доставлен "внешней" стороне (подумайте о написании библиотеки для клиента).

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

"предотвращение установки пользователями внутренних данных компонента в недопустимое или несогласованное состояние".

в то же время нельзя сказать, что

"пользователям компонента (других частей программного обеспечения) нужно только знать, что делает компонент, и нельзя ставить себя в зависимость от деталей того, как это делается".

обе цитаты из Википедия.

чтобы дать конкретный пример: мне нужно доставить клиентскую DLL, которая упрощает и скрывает связь со службой WCF (по сути, удаленный фасад). Поскольку это зависит от 3 разных прокси-классов WCF, если я использую подход DI, я вынужден предоставлять их через конструктор. С этим я выставляю внутренности моего уровень связи, который я пытаюсь скрыть.

вообще я все за Ди. В этом конкретном (крайнем) примере это кажется мне опасным.

Я боролся с этим понятием. Во-первых, "требование", чтобы использовать контейнер (как весной), чтобы создать экземпляр объекта почувствовал, как прыжки через обручи. Но на самом деле это действительно не обруч - это просто еще один "опубликованный" способ создания объектов, которые мне нужны. Конечно, инкапсуляция "сломана", потому что кто - то "вне класса" знает, что ему нужно, но это действительно не остальная часть системы, которая знает это-это контейнер DI. Ничего волшебного не происходит иначе, потому что Ди "знает" один объект нуждается в другом.

на самом деле это становится еще лучше - сосредоточившись на фабриках и хранилищах, мне даже не нужно знать, что DI участвует вообще! Это для меня ставит крышку обратно на инкапсуляцию. Фу!

Я верю в простоту. Применение инъекции IOC / Dependecy в доменных классах не делает никаких улучшений, за исключением того, что код намного сложнее поддерживать, имея внешние xml-файлы, описывающие отношение. Многие технологии, такие как EJB 1.0/2.0 & struts 1.1, возвращаются назад, уменьшая материал, помещенный в XML, и пытаются поместить их в код как раздражение и т. д. Поэтому применение IOC для всех классов, которые вы разрабатываете, сделает код бессмысленным.

МОК имеет его преимущества, когда зависимый объект не готов к созданию во время компиляции. Это может произойти в большинстве компонентов архитектуры абстрактного уровня инфраструктуры, пытаясь установить общую базовую структуру, которая может потребоваться для работы в различных сценариях. В этих местах использование МОК имеет больше смысла. Тем не менее это не делает код более простым / ремонтопригодным.

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

PS. Путем предоставления Инъекции Зависимостей вы не обязательно перерыв инкапсуляция. Пример:

obj.inject_dependency(  factory.get_instance_of_unknown_class(x)  );

клиентский код еще не знает деталей реализации.

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

кроме того, с помощью какой-то функции autowiring (Autofac для C#, например), это делает код очень чистый. Построив методы расширения для Autofac builder, я смог вырезать много кода конфигурации DI, который мне пришлось бы поддерживать с течением времени по мере роста списка зависимостей.

Я согласен, что доведенный до крайности, DI может нарушить инкапсуляцию. Обычно DI предоставляет зависимости, которые никогда не были по-настоящему инкапсулированы. Вот упрощенный пример, заимствованный из Мишко Hevery это синглтоны-патологические лжецы:

вы начинаете с теста кредитной карты и пишете простой модульный тест.

@Test
public void creditCard_Charge()
{
    CreditCard c = new CreditCard("1234 5678 9012 3456", 5, 2008);
    c.charge(100);
}

в следующем месяце вы получите счет на $100. Почему тебе предъявили обвинение? Модульный тест повлиял на производственную базу данных. Внутренне, кредитные карты звонки Database.getInstance(). Рефакторинг кредитной карты так, что она принимает DatabaseInterface в своем конструкторе раскрывает тот факт, что есть зависимость. Но я бы сказал, что зависимость никогда не была инкапсулирована, так как класс CreditCard вызывает внешне видимые побочные эффекты. Если вы хотите протестировать кредитную карту без рефакторинга, вы, безусловно, можете наблюдать зависимость.

@Before
public void setUp()
{
    Database.setInstance(new MockDatabase());
}

@After
public void tearDown()
{
    Database.resetInstance();
}

Я не думаю, что стоит беспокоиться о том, что предоставление базы данных в качестве зависимости уменьшает инкапсуляцию, потому что это хороший дизайн. Не все решения DI будут так прямолинейны. Однако ни один из других ответов не показывает встречного примера.

Я думаю, что это вопрос масштаба. Когда вы определяете инкапсуляцию (не давая знать, как), вы должны определить, что такое инкапсулированная функциональность.

  1. класс а: то, что вы инкапсулируете, является единственной ответственностью класса. Что он умеет делать. Например, сортировка. Если вы вводите какой-то компаратор для заказа, скажем, клиентов, это не часть инкапсулированной вещи: quicksort.

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

когда вы программируете классы, это, реализуя отдельные обязанности в классы, вы используете вариант 1.

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

это реализация настроенного экземпляра:

<bean id="clientSorter" class="QuickSort">
   <property name="comparator">
      <bean class="ClientComparator"/>
   </property>
</bean>

вот как некоторые другие клиентские коды используют его:

<bean id="clientService" class"...">
   <property name="sorter" ref="clientSorter"/>
</bean>

Он инкапсулируется, потому что, если вы измените реализацию (изменения clientSorter определение компонента) не использовать клиент. Может быть, как вы используете xml-файлы со всеми написанными вместе вы видите все детали. Но поверьте, клиентский код (ClientService) не знаю ничего о его сортировщик.

вероятно, стоит упомянуть, что Encapsulation несколько зависит от перспективы.

public class A { 
    private B b;

    public A() {
        this.b = new B();
    }
}


public class A { 
    private B b;

    public A(B b) {
        this.b = b;
    }
}

С точки зрения того, кто работает над A класс, во втором примере A знает намного меньше о природе this.b

тогда как без Ди

new A()

vs

new A(new B())

человек, глядя на этот код знает больше о природе A во втором примере.

С ди, по крайней мере, все что слил знания в одном месте.

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

  1. Это затрудняет повторное использование кода. Модуль, который клиент может использовать без явного предоставления зависимостей, очевидно, проще в использовании, чем тот, где клиент должен каким-то образом узнать, что такое зависимости этого компонента, а затем каким-то образом сделать их доступными. Например, компонент первоначально созданный для использования в приложении ASP может ожидать, что его зависимости будут предоставлены контейнером DI, который предоставляет экземплярам объектов время жизни, связанное с клиентскими http-запросами. Это может быть непросто воспроизвести в другом клиенте, который не поставляется с тем же встроенным контейнером DI, что и исходное приложение ASP.

  2. Это может сделать код более хрупким. Зависимости, предоставляемые спецификацией интерфейса, могут быть реализованы неожиданными способами, что дает поднимитесь до целого класса ошибок времени выполнения, которые невозможны со статически разрешенной конкретной зависимостью.

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

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

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

по моему опыту основная проблема в отношении использования DI заключается в том, что независимо от того, начинаете ли вы с фреймворка приложения со встроенным DI, или вы добавляете поддержку DI в свою кодовую базу, по какой-то причине люди предполагают, что поскольку у вас есть поддержка DI, это должен быть правильный способ создания экземпляра все. Они просто никогда даже не потрудились задать вопрос "Должна ли эта зависимость быть внешне указано?". И что еще хуже, они также начинают пытаться заставить всех остальных использовать поддержку DI для все тоже.

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

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

DI нарушает инкапсуляцию для не совместно используемых объектов-период. Общие объекты имеют срок службы вне создаваемого объекта и поэтому должны быть объединены в создаваемый объект. Объекты, которые являются частными для создаваемого объекта, должны быть составлены в созданный объект - когда созданный объект уничтожается, он берет с собой составленный объект. Возьмем в качестве примера человеческое тело. Что составлено и что агрегировано. Если бы мы использовали DI, конструктор человеческого тела бы 100 объектов. Многие органы, например, являются (потенциально) сменный. Но, они все еще составлены в тело. Клетки крови создаются в организме (и разрушаются) ежедневно, без необходимости внешних воздействий (кроме белковых). Таким образом, клетки крови создаются внутри организма - новые клетки крови().

защитники DI утверждают, что объект никогда не должен использовать новый оператор. Этот "пуристский" подход не только нарушает инкапсуляцию, но и Лисков Принцип замещения для того, кто создает объект.