Каковы недостатки использования Dependency Injection? [закрыто]


336

Я пытаюсь представить DI как образец здесь на работе, и один из наших ведущих разработчиков хотел бы знать: Каковы - если таковые имеются - недостатки в использовании шаблона внедрения зависимостей?

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


Пояснение : я говорю о паттерне внедрения зависимостей (см. Эту статью Мартина Фаулера), а не о конкретной платформе, будь то на основе XML (например, Spring) или на основе кода (например, Guice), или «самообращаемая» ,


Изменить : Некоторые большие дальнейшие обсуждения / разглагольствования / дебаты продолжаются / г / программирование здесь.


Возможно, было бы неплохо указать, следует ли нам обсуждать сам DI или какой-то конкретный тип инструмента, который его поддерживает (на основе XML или нет).
Джесси Милликен

5
Разница в том, что я НЕ ищу ответы, которые относятся только к конкретным фреймворкам, таким как «XML sucks». :) Я ищу ответы, которые относятся к концепции DI, как обрисовано в общих чертах Фаулером здесь: martinfowler.com/articles/injection.html
Epaga

3
Я вообще не вижу недостатков в DI (конструктор, сеттер или метод). Это развязывает и упрощает без накладных расходов.
Киссаки

2
@ Kissaki хорошо, кроме того, что сеттер вводит вещи. Лучше всего делать объекты, которые можно использовать при строительстве. Если вы мне не верите, попробуйте добавить еще одного соавтора к объекту с помощью инъекции 'setter', вы наверняка получите NPE ....
time4tea

Ответы:


209

Пара моментов:

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

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


72
- Разделение классов уменьшает сложность. Многие классы не делают приложение сложным. - У вас должна быть зависимость только от вашей структуры DI в корне приложения.
Роберт

91
Мы люди; наша кратковременная память ограничена и не может обрабатывать много <xxx> одновременно. Это то же самое для классов, как и для методов, функций, файлов или любых конструкций, которые вы используете для разработки своей программы.
Håvard S

45
@Havard S: Да, но 5 действительно сложных классов не проще, чем 15 простых. Способ уменьшить сложность состоит в том, чтобы уменьшить рабочий набор, требуемый программисту, работающему над кодом - сложные, сильно взаимозависимые классы не достигают этого.
кёрю

35
@kyoryu Я думаю, что мы все согласны с этим. Имейте в виду, что сложность не такая же, как сцепление . Уменьшение сцепления может увеличить сложность. Я действительно не говорю, что DI - плохая вещь, потому что это увеличивает количество классов, я выделяю потенциальные недостатки, связанные с этим. :)
Håvard S

96
+1 для "DI увеличивает сложность" Это верно в DI и за его пределами. (Почти) каждый раз, когда мы увеличиваем гибкость, мы собираемся увеличивать сложность. Все дело в балансе, который начинается с знания плюсов и минусов. Когда люди говорят: «минусов нет», это верный показатель того, что они еще не полностью поняли это.
Дон Брэнсон

183

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

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

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

Соответствующие ссылки

http://thedailywtf.com/Articles/The_Inner-Platform_Effect.aspx

http://www.joelonsoftware.com/articles/fog0000000018.html


Вероятно, самая простая форма внедрения зависимости (не смейтесь) - это параметр. Зависимый код зависит от данных, и эти данные вводятся посредством передачи параметра.

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

Давайте возьмем эту простую традиционную функцию - синтаксис C ++ здесь не имеет значения, но я должен как-то это прописать ...

void Say_Hello_World ()
{
  std::cout << "Hello World" << std::endl;
}

У меня есть зависимость, которую я хочу извлечь и вставить - текст «Hello World». Достаточно просто ...

void Say_Something (const char *p_text)
{
  std::cout << p_text << std::endl;
}

Как это более негибко, чем оригинал? Что ж, если я решу, что на выходе должен быть Unicode. Я, вероятно, хочу переключиться с std :: cout на std :: wcout. Но это означает, что мои строки должны быть wchar_t, а не char. Либо каждый вызывающий объект должен быть изменен, либо (более разумно) старая реализация заменяется адаптером, который переводит строку и вызывает новую реализацию.

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

И если это кажется тривиальным, взгляните на эту реальную функцию из Win32 API ...

http://msdn.microsoft.com/en-us/library/ms632680%28v=vs.85%29.aspx

Это 12 "зависимостей" для решения. Например, если разрешение экрана становится действительно огромным, возможно, нам понадобятся 64-битные значения координат - и другая версия CreateWindowEx. И да, уже есть старая версия, которая все еще висит вокруг, которая предположительно отображается за кулисами новой версии ...

http://msdn.microsoft.com/en-us/library/ms632679%28v=vs.85%29.aspx

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

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

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


3
Внедрение зависимостей не имеет ни одного из этих недостатков. Это только изменяет способ передачи ваших зависимостей объектам, что не добавляет сложности и негибкости. Наоборот.
Киссаки

24
@ Kissaki - да, это меняет способ передачи ваших зависимостей. И вы должны написать код для обработки этой передачи зависимостей. И прежде чем делать это, вы должны выяснить, какие зависимости передать, определить их так, чтобы это имело смысл для того, кто не кодировал зависимый код, и т. Д. Вы можете избежать воздействия во время выполнения (например, используя параметры политики в шаблонах). в C ++), но это все еще код, который вы должны писать и поддерживать. Если это оправдано, это очень маленькая цена за большое вознаграждение, но если вы притворяетесь, что нет цены, которую нужно платить, это просто означает, что вы будете платить эту цену, когда нет прибыли.
Steve314

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

Я не уверен, что понимаю этот пример, если учесть перегрузку. Для вывода литерала «Hello World» мы используем подпись (). Чтобы вывести 8-битную строку, используйте подпись (char). Для вывода юникода используется перегрузка (wchar_t). Очевидно, что в этом случае (wchar_t) является тем, что другие вызывают за кадром. Там не нужно много переписывать код. Я что-то упускаю?
ингридиент_15939

2
@ ингридиент_15939 - да, это упрощенный пример с игрушкой. Если бы вы действительно это делали, вы бы просто использовали перегрузку, а не адаптер, так как стоимость дублирования кода меньше стоимости адаптера. Но обратите внимание, что дублирование кода, как правило, плохо, и использование адаптеров, чтобы избежать дублирования кода, является еще одним хорошим методом (который иногда может использоваться слишком много и в неправильных местах).
Steve314

77

Вот моя собственная первоначальная реакция: в основном те же самые недостатки любой модели.

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

2
Почему учиться нужно время? Я думал, что это делает вещи проще по сравнению с EJB.
fastcodejava

8
Это кажется волнистым, учитывая, что вы не хотите субъективного обсуждения. Я предлагаю создать (коллективно, если необходимо) реалистичный пример для изучения. Создание образца без DI было бы хорошей отправной точкой. Тогда мы могли бы оспорить это в отношении изменений требований и изучить влияние. (Между прочим, это очень удобно для меня, когда я собираюсь ложиться спать.)
Джесси Милликен

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

4
Я не считаю ДИ паттерном. Это (в сочетании с IoC) действительно больше похоже на модель программирования - если вы полностью следуете им, ваш код будет выглядеть намного больше как актерский, чем «типичный» псевдо-процедурный ОО.
кёрю

1
+1 Я использую Symfony 2, DI во всем коде пользователя, просто исчерпывает его использование.
MGP

45

Самым большим «недостатком» Inversion of Control (не совсем DI, но достаточно близко) является то, что он имеет тенденцию устранять наличие единой точки для просмотра обзора алгоритма. Это в основном то, что происходит, когда вы развязываете код, хотя - возможность искать в одном месте - это артефакт тесной связи.


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

3
Вот почему слово «оборотная сторона» было в кавычках :) Слабая связь и сильная инкапсуляция исключают «одно место, чтобы увидеть все», своего рода по определению. См. Мои комментарии к ответу Хаварда С. Если у вас возникнет ощущение, что я против DI / IoC.
kyoryu

1
Я согласен (я даже проголосовал за тебя). Я просто делал точку
Викирк

1
@vickirk: Недостатком, я думаю, является то, что человеку трудно понять. Разделение вещей увеличивает сложность в том смысле, что людям труднее понять, и для полного понимания требуется больше времени.
Бьярке Фрейнд-Хансен

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

42

7
Мне любопытно, когда кто-то спрашивает о wonsides, почему ответы в духе "нет ни одного" получают голосование и те, которые содержат некоторую информацию, связанную с вопросом, нет?
Габриэль Шербак

12
-1 потому что я не ищу коллекцию ссылок, я ищу реальные ответы: как насчет суммирования баллов каждой статьи? Тогда я бы проголосовал вместо этого. Обратите внимание, что сами статьи довольно интересны, хотя кажется, что первые две на самом деле развенчивают негативы DI?
Epaga

4
Интересно, за самый проголосовавший ответ также проголосовали, потому что он действительно вообще не отвечает на вопрос imho :) Конечно, я знаю, что вы спросили, и что я ответил, я не спрашиваю об ответах, я просто смущен, почему мой ответ не помогает, хотя очевидно, что недостатком DI является то, что слишком круто очень помогает.
Габриэль Шербак

41

Последние 6 месяцев я активно использую Guice (Java DI framework). Хотя в целом я думаю, что это здорово (особенно с точки зрения тестирования), есть определенные недостатки. Наиболее заметно:

  • Код может стать сложнее для понимания. Инъекция зависимости может использоваться очень ... творчески ... способами. Например, я только что наткнулся на некоторый код, который использовал пользовательскую аннотацию для внедрения определенных IOStreams (например: @ Server1Stream, @ Server2Stream). Хотя это работает, и я должен признать, что имеет определенную элегантность, это делает понимание инъекций Guice необходимым условием для понимания кода.
  • Более высокая кривая обучения при изучении проекта. Это относится к пункту 1. Чтобы понять, как работает проект, использующий внедрение зависимостей, необходимо понимать как шаблон внедрения зависимостей, так и конкретную среду. Когда я приступил к своей нынешней работе, я потратил довольно много смущенных часов, пытаясь понять, что делает Guice за кулисами.
  • Конструкторы становятся большими. Хотя это может быть в значительной степени решено с помощью конструктора по умолчанию или фабрики.
  • Ошибки могут быть запутаны. Моим последним примером этого было столкновение с двумя именами флагов. Guice молча проглотил ошибку, и один из моих флагов не был инициализирован.
  • Ошибки передаются во время выполнения. Если вы неправильно настроили свой модуль Guice (циклическая ссылка, плохая привязка, ...), большинство ошибок не будут обнаружены во время компиляции. Вместо этого ошибки выявляются при фактическом запуске программы.

Теперь, когда я пожаловался. Позвольте мне сказать, что я буду продолжать (охотно) использовать Guice в моем текущем проекте и, скорее всего, в следующем. Внедрение зависимостей - отличный и невероятно мощный паттерн. Но это определенно может сбить с толку, и вы почти наверняка потратите некоторое время на проклятие в любой среде внедрения зависимостей, которую вы выберете.

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


14
Я не понимаю, почему люди не делают большую фразу «Ошибки выдвигаются во время выполнения», для меня это прерыватель сделки, статическая ошибка типизации и время компиляции - самый большой подарок, данный разработчикам, я бы не стал их прочь ни за что
Ричард Тингл

2
@RichardTingle, IMO во время запуска приложения Первоначально инициализируются модули DI, поэтому любая неправильная конфигурация в модуле будет видна сразу после запуска приложения, а не через несколько дней или времени. Также возможно загружать модули постепенно, но если мы придерживаемся духа DI, ограничивая загрузку модуля перед инициализацией логики приложения, мы можем успешно изолировать плохие привязки при запуске приложения. Но если мы настроим его как шаблон поиска сервисов, то эти плохие привязки наверняка станут сюрпризом.
k4vin

@RichardTingle Я понимаю, что это никогда не будет похоже на сеть безопасности, предоставляемую компилятором, но DI как инструмент, используемый правильно, как указано в поле, тогда эти ошибки времени выполнения ограничиваются инициализацией приложения. Тогда мы можем рассматривать инициализацию приложения как своего рода этап компиляции для модулей DI. По моему опыту, в большинстве случаев, если приложение запускается, в нем не будет плохих привязок или неправильных ссылок. PS - Я использовал NInject с C #
k4vin

@RichardTingle - я согласен с вами, но это компромисс для получения слабосвязанного, поэтому тестируемого кода. И, как сказал k4vin, при инициализации обнаруживаются недостающие зависимости, и использование интерфейсов все равно поможет с ошибками времени компиляции.
Андрей

1
Я бы добавил «Трудно содержать код в чистоте» в вашем списке. Вы никогда не знаете, сможете ли вы удалить регистрацию, прежде чем тщательно протестировать код без нее.
Fernacolo

24

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

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

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

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


1
Да, мне нравится этот термин "код равиоли"; Я выражаю это более скучно, когда говорю о недостатках DI. Тем не менее, я не могу представить разработку какой-либо реальной инфраструктуры или приложения на Java без DI.
Говард М. Льюис Корабль

13

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

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


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

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

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

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


13

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

Это может быть важно, когда зависимость не часто используется в классе потребления; такие как что-то вроде IExceptionLogHandlerService. Очевидно, что такая служба вызывается (надеюсь, :)) редко в классе - предположительно, только при исключениях, которые необходимо регистрировать; все же канонический образец инжектора конструктора ...

Public Class MyClass
    Private ReadOnly mExLogHandlerService As IExceptionLogHandlerService

    Public Sub New(exLogHandlerService As IExceptionLogHandlerService)
        Me.mExLogHandlerService = exLogHandlerService
    End Sub

     ...
End Class

... требует предоставления "живого" экземпляра этой услуги, черт побери, стоимость / побочные эффекты, необходимые для его получения. Вероятно, это не так, но что, если создание этого экземпляра зависимости потребовало обращения к службе / базе данных, поиска файлов конфигурации или блокировки ресурса до его удаления? Если бы вместо этого этот сервис создавался по мере необходимости, размещался в сервисе или создавался на заводе (у всех были свои проблемы), то вы брали бы стоимость строительства только тогда, когда это необходимо.

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

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


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

1
Любой современный контейнер IoC позволит вам указать время жизни объекта для определенного абстрактного типа / интерфейса (всегда уникального, singleton, http scoped и т. Д.). Вы также можете предоставить фабричный метод / делегат для его ленивой обработки, используя Func <T> или Lazy <T>.
Дмитрий С.

Пятно на. Создание зависимостей излишне стоит памяти. Я обычно использую «ленивую загрузку» с геттерами, которые создают экземпляр класса только при вызове. Вы можете предоставить конструктор по умолчанию без параметров, а также последующие конструкторы, которые принимают инстанцированные зависимости. В первом случае, например, класс, реализующий IErrorLogger, создается только this.errorLogger.WriteError(ex)тогда, когда в операторе try / catch возникает ошибка.
Джон Бонфардеси

12

Иллюзия того, что вы развязали свой код, просто внедрив зависимость без АКТУАЛЬНОЙ развязки. Я думаю, что это самая опасная вещь в DI.


11

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

В частности, если вы нажмете Control-Click / Command-Click при вызове метода в коде, вы перейдете к объявлению метода в интерфейсе вместо конкретной реализации.

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

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

На самом деле, я рискну предположить, что для каждого «недостатка», который вы можете найти для внедрения зависимости, вы найдете много преимуществ, которые намного перевешивают его.


5
Ctrl-shift-B с resharper принимает вас реализации
Адриан

1
Но с другой стороны, у нас (в идеальном мире) не было бы по крайней мере 2 реализаций всего, то есть, по крайней мере, одной реальной реализации и фиктивной для модульного тестирования ;-)
vickirk

1
В Eclipse, если вы удерживаете Ctrl и наводите курсор на код вызова метода, он покажет вам меню, чтобы открыть либо декларацию, либо реализацию.
Сэм Хаслер

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

10

Внедрение зависимостей на основе конструктора (без помощи магических «структур») - это чистый и полезный способ структурировать ОО-код. В лучших кодовых базах, которые я видел, за годы, проведенные с другими бывшими коллегами Мартина Фаулера, я начал замечать, что большинство хороших классов, написанных таким образом, в конечном итоге имеют один doSomethingметод.

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


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

1
Ха - ха. Если вы сделаете это слишком много, вы скоро увидите, что ваш код дрянной, и вы проведете его рефакторинг. Кроме того, Ctrl-F6 не так сложно.
time4tea

И, конечно, верно и обратное: это всего лишь дурацкий длинный и функциональный способ написания классов в виде замыканий для получения преимуществ ОО-программирования
CurtainDog

Много лет назад я построил объектную систему с использованием замыканий в Perl, так что я понял, что вы говорите, но инкапсуляция данных не является уникальным преимуществом ОО-программирования, поэтому неясно, каковы эти полезные функции ОО, которые были бы сопоставимы клудги и длинноруки для получения на функциональном языке.
sanityinc

9

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

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

(Как примечание, я считаю, что вся тема довольно "религиозная", но ваш пробег будет зависеть от уровня технического фанатизма в вашей команде разработчиков!)


8
Если у вас есть большие уродливые конструкторы, может быть, ваши классы слишком большие и вам просто нужно много зависимостей?
Роберт

4
Это, безусловно, возможность, которую я хочу развлекать! ... К сожалению, у меня нет команды, с которой я могу делать рецензии. скрипки тихо играют в фоновом режиме, а затем экран становится черным ...
Джеймс Б.

1
Как грустит Марк Симан выше «Это может быть опасно для вашей карьеры» ...
Роберт

У меня не было ни малейшего понятия, что повествование может быть настолько выразительным: P
Anurag

@ Роберт, я пытаюсь найти цитату, где выше он это сказал?
Лян

8

Если вы используете DI без контейнера IOC, самым большим недостатком является то, что вы быстро видите, сколько на самом деле зависимостей в вашем коде и насколько тесно все на самом деле. («Но я подумал, что это хороший дизайн!») Естественное продвижение вперед - это переход к контейнеру IOC, который может занять немного времени для изучения и реализации (не так плохо, как кривая обучения WPF, но это не бесплатно. либо). Последним недостатком является то, что некоторые разработчики начнут писать честные и добрые юнит-тесты, и им потребуется время, чтобы понять это. Разработчики, которые раньше могли что-то провернуть за полдня, внезапно потратят два дня, пытаясь понять, как высмеивать все их зависимости.

Как и в случае с ответом Марка Симанна, суть в том, что вы тратите время на то, чтобы стать лучшим разработчиком, а не взламывать куски кода вместе и бросать его за дверь / в производство. Что бы ваш бизнес предпочел? Только ты можешь ответить на это.


Хорошая точка зрения. Это плохое качество / быстрая доставка против хорошего качества / медленная доставка. Обратная сторона? ДИ не магия, ожидание того, что это будет недостатком.
Лян

5

DI - это техника или шаблон, не связанные с какой-либо структурой. Вы можете подключить свои зависимости вручную. DI помогает вам с SR (Единая ответственность) и SoC (разделение интересов). DI приводит к лучшему дизайну. С моей точки зрения и опыта нет минусов . Как и с любым другим шаблоном, вы можете ошибиться или использовать его неправильно (но в случае DI это довольно сложно).

Если вы вводите DI в качестве принципа для унаследованного приложения с использованием фреймворка - самая большая ошибка, которую вы можете сделать, - это неправильно использовать его как Service-Locater. DI + Framework сам по себе великолепен и только улучшал его везде, где я его видел! С организационной точки зрения, есть общие проблемы с каждым новым процессом, техникой, шаблоном, ...:

  • Вы должны тренировать свою команду
  • Вы должны изменить свою заявку (включая риски)

В общем, вы должны вкладывать время и деньги , кроме того, нет никаких минусов, на самом деле!


3

Читаемость кода. Вы не сможете легко понять поток кода, поскольку зависимости скрыты в файлах XML.


9
Вам не нужно использовать файлы конфигурации XML для внедрения зависимостей - выберите контейнер DI, который поддерживает конфигурацию в коде.
Håvard S

8
Внедрение зависимостей не обязательно подразумевает XML-файлы. Кажется, что это все еще может иметь место в Java, но в .NET мы отказались от этой связи много лет назад.
Марк Симанн

6
Или вообще не используйте DI-контейнер.
Джесси Милликен

если вы знаете, что код использует DI, вы можете легко предположить, кто устанавливает зависимости.
Божо

Например, в Java для Google Guice не нужны файлы XML.
Epaga

2

Две вещи:

  • Им требуется дополнительная поддержка инструмента для проверки правильности конфигурации.

Например, IntelliJ (коммерческая версия) поддерживает проверку допустимости конфигурации Spring и будет отмечать ошибки, такие как нарушения типов в конфигурации. Без поддержки такого типа вы не сможете проверить правильность конфигурации перед запуском тестов.

Это одна из причин, почему шаблон «торт» (как он известен сообществу Scala) является хорошей идеей: проводку между компонентами можно проверить с помощью средства проверки типов. У вас нет такой выгоды с аннотациями или XML.

  • Это делает глобальный статический анализ программ очень сложным.

Такие фреймворки, как Spring или Guice, затрудняют статически определить, как будет выглядеть граф объектов, созданный контейнером. Хотя они создают граф объектов при запуске контейнера, они не предоставляют полезных API, которые описывают граф объектов, который / будет / создан.


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

Правда, хорошая мысль. Я должен был быть более ясным, что я говорил о проблемах с структурами DI, а не о паттерне. Возможно, я пропустил разъяснение вопроса, когда я ответил (предполагая, что это было там в то время).
Мартин Эллис

Ах. В этом случае я радостно убираю свое раздражение. Извинения.
time4tea

2

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


1

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


3
Просто чтобы дать цифру, контейнер DI должен разрешать несколько тысяч зависимостей в секунду. ( codinginstinct.com/2008/05/… ) Контейнеры DI допускают отложенное создание экземпляров. Производительность не должна быть проблемой (даже в огромных приложениях). Или, по крайней мере, проблема производительности должна быть решена и не должна служить основанием для принятия решения против IoC и его структур.
Роберт
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.