[design-patterns] Каковы недостатки использования инъекций зависимостей?


Answers

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

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

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

Связанные ссылки

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

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

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

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

Question

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

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

Уточнение : я говорю об шаблоне Injection Dependency (см. Эту статью Мартина Фаулера), а не о конкретной структуре, будь то основанной на XML (например, Spring) или основанной на кодах (например, Guice) или «самокаталированной», ,

Редактирование : Некоторые другие дальнейшие обсуждения / разглашение / дебаты идут /r/programming здесь.




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




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

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

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

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

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

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




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




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

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

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




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

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

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

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

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




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

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




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




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

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

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

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




Links