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



9 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

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

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

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

design-patterns dependency-injection

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

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

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

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




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




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

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

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

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




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

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

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

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

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

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




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

Если это может быть существенным, тогда, когда зависимость не часто используется в классе потребления; например, как 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> зависимостей, например, предоставив классу Lazy<IService> экземпляр в качестве зависимости. Это изменит конструкторы зависимых объектов и сделает еще более осведомленным о деталях реализации, таких как затраты на строительство объектов, что тоже нежелательно.




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




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

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




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




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




Related