Конструктор по умолчанию против контейнера IOC
-
01-07-2019 - |
Вопрос
Может ли кто-нибудь объяснить мне преимущества использования контейнера IOC по сравнению с простым жестким кодированием реализации по умолчанию в конструктор по умолчанию?
Другими словами, что не так в этом коде?
public class MyClass
{
private IMyInterface _myInterface;
public MyClass()
{
_myInterface = new DefaultMyInterface();
}
public MyClass(IMyInterface myInterface)
{
_myInterface = myInterface;
}
}
Насколько я могу судить, этот класс достаточно поддерживает внедрение конструктора, поэтому легко выполнить модульное тестирование и макетирование.В дополнение к этому, конструктор по умолчанию устраняет вычислительные затраты контейнера IOC (не говоря уже о том, что весь процесс становится намного более прозрачным).
Единственное преимущество, которое я вижу в использовании контейнера IOC, — это необходимость часто отключать реализацию своих интерфейсов.Я что-то пропустил?
Решение
Идея IoC состоит в том, чтобы делегировать часть функциональности вашего компонента другой части системы.В мире IoC есть компоненты, которые не знают друг о друге.Ваш пример нарушает это, поскольку вы создаете тесную связь между MyClass и некоторой реализацией IMyInterface. Главная идея это твой компонент не имеет знаний о том, как оно будет использоваться.В вашем примере ваш компонент делает некоторые предположения о его использовании.
На самом деле этот подход может работать, но смешивание IoC и явной инициализации объекта не является хорошей практикой, по моему мнению.
IoC дает вам слабую связь, выполняя позднее связывание за счет ясности кода.Когда вы добавляете в этот процесс дополнительное поведение, это еще больше усложняет ситуацию и может привести к ошибкам, когда некоторые компоненты потенциально могут получить объект с нежелательным или непредсказуемым поведением.
Другие советы
Выбирайте сторону :)
Короче говоря, МОК рекомендуется.Проблема с кодом заключается в том, что я не могу заменить реализацию зависимости по умолчанию без перекомпиляции кода, как вы сказали в конце.IOC позволяет вам изменять конфигурацию или состав вашего объекта во внешнем файле без перекомпиляции.
IOC берет на себя ответственность за «строительство и сборку» остальной части кода.Цель IOC не в том, чтобы сделать ваш код тестируемым...это приятный побочный эффект.(Точно так же, как код TDD приводит к лучшему дизайну)
В этом коде нет ничего плохого, и вы все равно можете использовать его с фреймворками внедрения зависимостей, такими как Spring и Guice.
Многие разработчики рассматривают XML-файл конфигурации Spring как преимущество перед связыванием зависимостей внутри кода, поскольку вы можете переключать реализации без необходимости этапа компиляции.Это преимущество фактически реализуется в ситуациях, когда у вас уже есть несколько реализаций, скомпилированных в вашем пути к классам, и вы хотите выбрать реализацию во время развертывания.Вы можете представить себе ситуацию, когда компонент после развертывания предоставляется третьей стороной.Аналогичным образом может возникнуть ситуация, когда вы захотите отправить дополнительные реализации в виде исправления после развертывания.
Но не все платформы DI используют конфигурацию XML.Например, в Google Guice есть модули, написанные как классы Java, которые необходимо скомпилировать, как любой другой класс Java.
Так в чем же преимущество DI, если вам вообще нужен этап компиляции?Это возвращает нас к вашему первоначальному вопросу.Я вижу следующие преимущества:
- Стандартный подход к внедрению зависимостей во всем приложении.
- Конфигурация аккуратно отделена от остальной логики.
- Возможность внедрения прокси.Например, Spring позволяет вам выполнять декларативную обработку транзакций путем внедрения прокси вместо вашей реализации.
- Упрощенное повторное использование логики конфигурации.Если вы активно используете DI, вы увидите сложное дерево зависимостей, развивающееся с течением времени.Управление им без четко выделенного уровня конфигурации и поддержки инфраструктуры может оказаться кошмаром.Платформы DI упрощают повторное использование логики конфигурации посредством наследования и других средств.
Единственное, что меня беспокоит, это (1) если ваша зависимость от службы по умолчанию сама имеет зависимость от другой/вторичной службы (и так далее... ваш DefaultMyInterface зависит от ILogger) и (2) вам необходимо изолировать первую зависимость службы от второй ( необходимо протестировать DefaultMyInterface с помощью заглушки ILogger).В этом случае вам, очевидно, придется потерять «новый DefaultMyInterface» по умолчанию и вместо этого выполнить одно из:(1) чистое внедрение зависимостей или (2) указатель службы или (3)Container.BuildUp(new DefaultMyInterface());
Некоторые опасения, перечисленные другими авторами, могут быть несправедливы по отношению к вашему вопросу.Вы не спрашивали о нескольких «производственных» реализациях.Вы спрашиваете о модульное тестирование. В случае модульного тестирования ваш подход, с учетом моего первого предостережения, кажется законным;Я бы тоже рассмотрел возможность использования его в простых модульных тестах.
Аналогичным образом, несколько респондентов выразили обеспокоенность по поводу повторяемости.Я тоже не люблю повторение, но если (1) ваша реализация по умолчанию действительно ваша по умолчанию (YAGNI:у вас нет планов по изменению значения по умолчанию) и (2) вы не верите, что первое изложенное мною предостережение применимо, и (3) предпочитаете более простой подход к коду, которым вы поделились, тогда я не думаю, что эта конкретная форма повторение - это проблема.
Помимо слабой связи, IoC уменьшит дублирование кода.Когда вы используете IoC и хотите изменить реализацию интерфейса по умолчанию, вам нужно изменить ее только в одном месте.Когда вы используете конструкторы по умолчанию для внедрения реализации по умолчанию, вам придется менять ее везде, где используется интерфейс.
В дополнение к другим комментариям в этих случаях можно было бы привести аргументы в пользу принципа DRY (Не повторяйся).Нет необходимости помещать этот код конструкции по умолчанию в каждый класс.Также введена обработка особых случаев, когда она не требуется.
Я не понимаю, почему вашу технику жесткого кодирования реализации по умолчанию нельзя использовать вместе с контейнером IOC.Просто зависимости, которые вы не указали в конфигурации, будут использовать реализацию по умолчанию.
Или я что-то упускаю?
Одной из причин, по которой вы можете использовать IOC-контейнер, является облегчение поздней настройки вашего программного обеспечения.
Скажем, например, вы предоставляете несколько реализаций определенного интерфейса — клиент (или ваша команда профессиональных услуг) может решить, какую из них использовать, изменив файл конфигурации IOC.