В этой части рассмотрим антишаблоны и способы исправления пораженного ими кода.
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
Это даже не антишаблон, а проявление нарушения принципа единственной ответственности. Его можно встретить при переходе к использованию внедрения зависимостей.
Признаки
- Конструктор объекта требует большое число внешних зависимостей.
Основная проблема
- Объект и интерфейс, который он реализует, скорее всего отвечает за несколько различных задач.
Способы решения
- Пересмотр обязанностей интерфейса и объекта, разделение его на несколько небольших, реализующих различные интерфейсы.
- Использование разных способов внедрения зависимостей:
- через свойство – для внутренних зависимостей;
- через параметр метода – если зависимость нужна только в одном методе.