Внедрение зависимостей — это круто. Реально круто. Но это ещё и ад.
Идея о том, чтобы IoC-контейнер разрешал все зависимости объектов явного конструктора, всячески поддерживается в сообществе разработчиков. Это отличная идея, которая упрощает разработку в режиме создания программы.
Подход здесь такой: создаётся класс, который принимает какие-то зависимости. Недолго думая, их регистрируют с помощью IOC-контейнера. И полный вперёд!
Не будем подробно останавливаться на IoC (инверсии управления), DIP (принципе инверсии зависимостей), внедрении зависимостей и контейнерах: уверен, вам уже знаком обширный список предоставляемых ими преимуществ.
Но есть и большая проблема.
Внедрение зависимостей способствует тому, что разработчики становятся ленивыми.Я бы даже сказал, умопомрачительно ленивыми.
Сейчас довольно часто можно наблюдать, как ни с того ни с сего классы service
вдруг превращаются в то, что лучше характеризируется словами «логические мусорные контейнеры». Всё больше становится раздуваний конструктора, и обычно это конструкторы классов service
. Бездумное добавление очередной зависимости явного конструктора прочно вошло в повседневную рутину инженера-разработчика.
От него регулярно требуется передавать шесть, семь или более объектов для простого вызова метода класса service
, в то время как последнему для корректного выполнения нужна лишь одна из этих зависимостей.
Между прочим, есть статья с несколькими идеями о том, как можно выполнить рефакторинг при таком раздувании.
Это ад
Думаю, что многие разработчики совершенно упускают из виду то, чем чревата такая практика раздувания конструктора. Даже при написании простейших модульных тестов теперь требуется либо создавать несколько объектов, либо использовать mock
-заглушки.
Наступает усталость от зависимостей.
Представьте, что у вас есть класс с восемью аргументами конструктора и нужно протестировать один метод. Написание модульного теста теперь настолько проблематично, что вы начинаете передавать значения null
в качестве аргументов конструктора. Ведь вы знаете, что семь других зависимостей ровным счётом ничего не делают при тестировании этого конкретного метода.
И это ещё не самое худшее! Реальная веселуха начинается, когда у вас зависимость в классе с раздутым конструктором. Например, класс service
, зависящий от ещё одного класса service
. Вы можете возразить, что это результат плохого дизайна. Так и есть. Плохой дизайн, ставший возможным из-за легкомысленного, бездумного внедрения зависимостей.
И кстати, рекомендую перестать использовать эти классы-сервисы services
, называемые также «логическими мусорными контейнерами». Они зачастую представляют собой лишь плохо структурированные интерфейсы, не приносящие никакой реальной пользы. Лучше попробуйте разделить свои классы-сервисы на несколько специализированных классов.
Я за явные зависимости, внедрение зависимостей, инверсию управления и все остальные полезные штуки
А вы, наверное, уже подумали, что я против всего этого.
Я против невероятной лени, порождающей привычку снова и снова добавлять один аргумент конструктора за другим. Только и всего.
Чтобы побороть эту плохую привычку, нужно всегда пытаться прикидывать, насколько трудным будет тестирование того или иного класса или метода. Сколько усилий потребуется, чтобы протестировать новый метод? Во скольких классах нужно задействовать mock-заглушки, а не сколько классов фактически используются?
Не следует добавлять аргумент конструктора случайно или в спешке.
Напутственные слова
Ну что, вы довольны своими классами, принимающими невероятное количество аргументов конструктора? Если да, тогда ничего не меняйте.
Возьмите из этой статьи всё, что покажется вам полезным, и проигнорируйте остальное.
Читайте также:
- 6 технологий, которые помогут стать востребованным фронтенд-разработчиком в 2021
- Почему я перешёл на Linux после 10 лет работы на Windows
- От рекомендаций по проведению ревью кода к общечеловеческим ценностям
Читайте нас в Telegram, VK и Яндекс.Дзен
Перевод статьи Nicklas Millard: Dependency Injection Makes You Incredibly Lazy