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

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

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

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

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

Признаки

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

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

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

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

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

Комментарии (2) -

Bastard Injection
Незнаю. Как, то кажется мне сомнительным, что это антипатерн.

По сравнению с альтернативами: внедрение через конструктор и Service Locator.
Bastard Injection видится мне самым сбалансированным вариантаом.

StorageProvider.GetStorage(LogProvider.GetLog(), FsProvider.GetFs(),

  ConfiguraionProfider.GetConfiguration(), GUIProvider.GetController(), и еще что-нибудь);
и так при каждом обращении. или
StorageProvider.GetStorage();
Неправда гораздо элегантнее.

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

А если меня вдруг заинтерсуют зависимости класса, то при использованиее Bastard Injection, их найти гораздо легче, чем при использожании Service Locator. Почему:
* Я могу использовать Intellisense  и увижу перегрузку коструктра со всеми параметрами
* Я перейду в конструктор и увижу все в одном месте

@ idealist: Давайте начнем с того, что приведенный пример не укладывается в описание Bastard Injection.

Теперь что касается примера. Тут возможно (подчеркну, возможно) есть нарушение SRP. И, возможно, стоит поглядеть в сторону Ambient Context. Как вариант, сделать IApplicationContext, который содержит интерфейсы конфига, лога и т.д.  Его внедрять обычным DI в конструкторе (чтобы эту зависимость было видно явно).

Ну а в таком контексте Service Locator - антипаттерн. Поэтому сравнивать его с Bastard Injection смысла нет.

Добавить комментарий