Внедрение зависимостей. Антишаблоны.

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

1. Control Freak

Антишаблон проектирования Control Freak проявляется в прямом создании и контроле времени жизни объектов. Т.е. напрямую противоречит идее Inversion Of Control.

Признаки

  • Непосредственное создание экземпляров зависимых классов.
  • Отказ от использования абстракций в пользу конкретных типов.

Основная проблема

  • Увеличение числа неявных зависимостей объекта.

Способы решения

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

2. Bastard Injection

Антишаблон Bastard Injection подразумевает создание конструктора по умолчанию, который порождает необходимые объекты и передает их в свой параметризированный вариант. Например, подобный подход можно встретить для внедрения объектов при модульном тестировании. При этом в самом приложении используются конструкторы по умолчанию.

Признаки

  • Наличие конструктора по умолчанию, который вызывает параметризированный.

Основная проблемы

  • Увеличение числа неявных зависимостей объекта.

Способы решения

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

3. Constrained Construction

Constrained Construction это наложение жестких ограничений на процесс создания объекта. Например, необходимость реализовывать в зависимом классе конструктор по умолчанию или с четко заданным для всех набором параметров. Данный антишаблон проявляется в месте создания объектов (Composition Root).

Признаки

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

Основная проблема

  • Привязка абстракции к конкретной реализации и, как следствие, потеря гибкости.

Способы решения

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

4. Service Locator

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

Признаки

  • Отсутствие явных зависимостей у объектов.
  • Наличие внутри кода методов запросов для получения реализаций тех или иных интерфейсов.

Основная проблема

Service Locator почти работает. Именно почти. Он решает все задачи, связанные с внедрением зависимостей. Главная и единственная его проблема – они все неявные. Глядя на сигнатуры конструкторов и методов класса нельзя определить использует ли он какие-либо зависимости или нет.

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

Способы решения

  • Отказ от использования Service Locator и переход к внедрению зависимостей через конструктор, свойство или параметр метода.

5. Single Responsibility Principle (SRP) Violation

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

Признаки

  • Конструктор объекта требует большое число внешних зависимостей.

Основная проблема

  • Объект и интерфейс, который он реализует, скорее всего отвечает за несколько различных задач.

Способы решения

  • Пересмотр обязанностей интерфейса и объекта, разделение его на несколько небольших, реализующих различные интерфейсы.
  • Использование разных способов внедрения зависимостей:
    • через свойство – для внутренних зависимостей;
    • через параметр метода – если зависимость нужна только в одном методе.