Pull to refresh

Dependency Injection: анти-паттерны

Reading time3 min
Views136K
Слабая связанность (low coupling) часто является признаком хорошо структурированной компьютерной системы и признаком хорошего дизайна. Wikipedia

Dependency Injection (DI) — это набор паттернов и принципов разработки програмного обеспечения, которые позволяют писать слабосвязный код. По мнению М.Фаулера, DI является разновидностью более глобального принципа инверсии управления (IoC), также известного как “Hollywood Principle”. Между тем, границы принципов внедрения зависимости достаточно размыты. Невозможно провести действительно четкую границу между этим и другими принципами написания качественного объектно-ориентированного кода. Например, принцип Dependency Inversion из SOLID, который часто путают с Dependency Injection, как бы подразумевает внедрение зависимостей, но им не ограничивается.

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

Анти-паттерн №1. «Control Freak». Руководитель-наркоман.

Все зависимости контролируются напрямую. Противоположность принципу инверсии управления.
Антипаттерн возникает всегда, когда мы внутри класса явно создаем изменяемую зависимость, используя ключевое слово new. Класс, который не отпускает контроль над своими зависимостями, является Control Freak.

Пример такого класса показан ниже:

private readonly ProductRepository repository;

public ProductService()
{
    string connectionString = ConfigurationManager.ConnectionStrings["Context"].ConnectionString;

    this.repository = new SqlProductRepository(connectionString);
}

Вариант избежать данного анти-паттерна: использовать Dependency Injection (желательно через конструктор). Корректный пример после рефакторинга, не являющийся Control Freak, выглядит следующим образом:

private readonly ProductRepository repository;

public ProductService(ProductRepository repository)
{
    if (repository == null) 
        throw new ArgumentNullException("repository");

    this.repository = repository;
}


Анти-паттерн №2. «Bastard Injection». Внебрачная зависимость.

Данный пункт подразумевает множественные перегрузки конструкторов вместе с «конструкторами по умолчанию», которые повсеместно встречаются в .Net, включая BCL. Основная проблема в том, что внешние зависимости обычно определены в других модулях и подобные конструкторы увеличивают связность системы буквально на пустом месте. Следует избегать внебрачных внешних зависимостей везде где возможно, а разрешение зависимостей отдать DI-контейнеру. Пример анти-паттерна приведен ниже:

private readonly ProductRepository repository;

public ProductService() : this(ProductService.CreateDefaultRepository())
{
}

public ProductService(ProductRepository repository)
{
    if (repository == null)
        throw new ArgumentNullException("repository");

    this.repository = repository;
}

private static ProductRepository CreateDefaultRepository()
{
    string connectionString = ConfigurationManager.ConnectionStrings["Context"].ConnectionString;
    return new SqlProductRepository(connectionString);
}

Как вариант избежать данного анти-паттерна, предлагается использовать Dependency Injection. Желательно опять же через конструктор, т.к. внедрение зависимостей через конструктор является наиболее корректным способом DI.

Анти-паттерн №3. «Constrained Construction». Ограниченное построение.

Анти-паттерн возникает, если существует требование ко всем зависимостям иметь «особенный» конструктор. Данное требование зачастую проистекает из желания «однотипно создавать через Reflection». Что-то типа:

string connectionString = ConfigurationManager.ConnectionStrings["Context"].ConnectionString;
string productRepositoryTypeName = ConfigurationManager.AppSettings["ProductRepositoryType"];
var productRepositoryType = Type.GetType(productRepositoryTypeName, true);
var repository = (ProductRepository)Activator.CreateInstance(productRepositoryType, connectionString);

Следует избегать данного анти-паттерна, а для однотипного создания классов использовать фабрики.

Анти-паттерн №4. «Service Locator». Сервис-локатор.

Анти-паттерн возникает при гранулированном получении отдельных сервисов в различных частях кода.
Автор перечисленных DI-анти-паттернов признает, что вопрос сервис-локатора дискуссионный, однако продолжает считать Service Locator анти-паттерном, аргументируя в основном тем, что бизнес-логика не должна знать об инфраструктурных вещах, одной из которых является сервис-локатор и все зависимости должны пробрасываться явно.

Список анти-паттернов DI приведен из книги “Dependency Injection in .NET”, за авторством Mark Seemann.
Tags:
Hubs:
Total votes 30: ↑23 and ↓7+16
Comments141

Articles