Информация об изменениях

Сообщение О "наивном" DI и об архитектурном бессилии от 27.07.2016 6:40

Изменено 21.09.2016 11:04 IQuerist

О "наивном" DI и об архитектурном бессилии

Никогда я не имел желания холиварить на тему DI, т.к. во всех проектах, где обнаруживалось страстное желание автора использовать DI и над которыми мне приходилось работать, использование DI выглядело как совершенно очевидный антипаттерн и мне всегда было абсолютно не понятно для чего автор тратит время и силы ничего не получая взамен?

Собственно ситуация имхо довольно типичная — мне то "опыт подказывает", а молодежь старательно "набивает шишки", но при этом, я почему-то сходу не нашел ни одного внятного объяснения от популяризаторов DI того, почему наивное использование DI будет провальным. Поэтому я решил таки сформулировать свой вариант

Посмотрим на типичный пример наивного DI:

public class HomeController : Controller

public HomeController(
IBoringItemDataReader,
IBoringItemDataWriter,
BoringItemChildDataReader,
IBoringItemChildDataWriter,
IAppDataJsonConverter,
...
)

Гиперболизируя можно было бы добавить еще IJsonConverter чтобы механизм конвертирования json можно было изменить и что ни будь вроде IDateTimeProvider, работа с датой и временем вряд ли поменяется, но на всякий случай стоит "уменьшить зависимости" .

Посмотрим на реализацию IBoringItemDataReader:

public class BoringItemDataReader : IBoringItemDataReader

и с десяток совершенно очевидных хелперных stateless методов типа:

GetBoringItemById
GetBoringItemByNumber
GetBoringItemByDataInterval
...

Ребята... вот эта низкоуровневая "требуха" это что ли "сервис"? Вот эти все "внутренности наружу" это инкапсуляция и архитектура? Как получилось, что попытка "соблюсти" принцип DIP очевидно вызывает нарушение всех остальных принципов SOLID и на это закрывают глаза? Как можно внедрять "зависимость сервиса" в ситуации, когда разработчик не способен создать сам внедряемый сервис? Проблема тут имхо базовая — разработчик не имеет навыков создания объектной декомпозиции и пытается компенсировать это использованием сложных механизмов, смысла которых он не понимает.

Лично для меня DI изначально был всего лишь удобным механизмом построения "плагинной архитектуры". Часть знакомых уверяла, что DI жизненно необходим для тестирования, но тут ситуация становилась совсем абсурдной, т.к. архитектурное решение (использовать DI) вроде как принималось, но никаких тестов при этом не было и в будущем они никогда не появлялись.

Как-то так... имхо главная начальная проблема DI — неспособность разработчиков создавать объектные декомпозиции. Это ведет к созданию абсолютно неправильной, очень низкоуровневой и хрупкой системы зависимостей нарушающей 4 из 5 принципов SOLID. Пример того, как некоторые пытаются лечить гланды через задний проход. Кстати неплохое название для антипаттерна — Colonoscopy Injection и для всей братии зомбированных шаблонами "наивных архитекторов" — архитект-проктолог.

Update:

Обсуждение натолкнуло на мысль, что на самом деле наивный DI нарушает принцип DIP в первую очередь. Т.к. каноничная формулировка DIP гласит:

    Модули верхних уровней не должны зависеть от модулей нижних уровней. Оба типа модулей должны зависеть от абстракций.
    Абстракции не должны зависеть от деталей. Детали должны зависеть от абстракций.

имхо совершенно очевидно, что наивный сервис слепленный из хелперных методов DAL, ни в коем случае не является "абстракцией". Он предельно конкретный с предельно конкретными entity типами даже если структура этих типов продублирована, а данные копируются с помощью мапперов (эдакий карго-культ "абстрации"). Имхо здесь совершенно четко определяемая проблемы — интерфейсы верхних уровней начинают определять низкоуровневые интерфейсы DAL. Разрозненные хелперные методы не перестанут быть хелперными методами от того, что их вызывают через интерфейс.
О "наивном" DI и об архитектурном бессилии

Никогда я не имел желания холиварить на тему DI, т.к. во всех проектах, где обнаруживалось страстное желание автора использовать DI и над которыми мне приходилось работать, использование DI выглядело как совершенно очевидный антипаттерн и мне всегда было абсолютно не понятно для чего автор тратит время и силы ничего не получая взамен?

Собственно ситуация имхо довольно типичная — мне то "опыт подказывает", а молодежь старательно "набивает шишки", но при этом, я почему-то сходу не нашел ни одного внятного объяснения от популяризаторов DI того, почему наивное использование DI будет провальным. Поэтому я решил таки сформулировать свой вариант

Посмотрим на типичный пример наивного DI:

public class HomeController : Controller

public HomeController(
IBoringItemDataReader,
IBoringItemDataWriter,
BoringItemChildDataReader,
IBoringItemChildDataWriter,
IAppDataJsonConverter,
...
)

Гиперболизируя можно было бы добавить еще IJsonConverter чтобы механизм конвертирования json можно было изменить и что ни будь вроде IDateTimeProvider, работа с датой и временем вряд ли поменяется, но на всякий случай стоит "уменьшить зависимости" .

Посмотрим на реализацию IBoringItemDataReader:

public class BoringItemDataReader : IBoringItemDataReader

и с десяток совершенно очевидных хелперных stateless методов типа:

GetBoringItemById
GetBoringItemByNumber
GetBoringItemByDataInterval
...

Ребята... вот эта низкоуровневая "требуха" это что ли "сервис"? Вот эти все "внутренности наружу" это инкапсуляция и архитектура? Как получилось, что попытка "соблюсти" принцип DIP очевидно вызывает нарушение всех остальных принципов SOLID и на это закрывают глаза? Как можно внедрять "зависимость сервиса" в ситуации, когда разработчик не способен создать сам внедряемый сервис? Проблема тут имхо базовая — разработчик не имеет навыков создания объектной декомпозиции и пытается компенсировать это использованием сложных механизмов, смысла которых он не понимает.

Лично для меня DI изначально был всего лишь удобным механизмом построения "плагинной архитектуры". Часть знакомых уверяла, что DI жизненно необходим для тестирования, но тут ситуация становилась совсем абсурдной, т.к. архитектурное решение (использовать DI) вроде как принималось, но никаких тестов при этом не было и в будущем они никогда не появлялись.

Как-то так... имхо главная начальная проблема DI — неспособность разработчиков создавать объектные декомпозиции. Это ведет к созданию абсолютно неправильной, очень низкоуровневой и хрупкой системы зависимостей нарушающей 4 из 5 принципов SOLID. Пример того, как некоторые пытаются лечить гланды через задний проход. Кстати неплохое название для антипаттерна — Colonoscopy Injection и для всей братии зомбированных шаблонами "наивных архитекторов" — архитект-проктолог.

Update:

Обсуждение натолкнуло на мысль, что на самом деле наивный DI нарушает принцип DIP в первую очередь. Т.к. каноничная формулировка DIP гласит:

    Модули верхних уровней не должны зависеть от модулей нижних уровней. Оба типа модулей должны зависеть от абстракций.
    Абстракции не должны зависеть от деталей. Детали должны зависеть от абстракций.

имхо совершенно очевидно, что наивный сервис слепленный из хелперных методов DAL, ни в коем случае не является "абстракцией". Он предельно конкретный с предельно конкретными entity типами даже если структура этих типов продублирована, а данные копируются с помощью мапперов (эдакий карго-культ "абстрации"). Имхо здесь совершенно четко определяемая проблемы — интерфейсы верхних уровней начинают определять низкоуровневые интерфейсы DAL. Разрозненные хелперные методы не перестанут быть хелперными методами от того, что их вызывают через интерфейс.

Update 2:

Не стоит ждать в этом топике борьбы за рассовую чистоту DI, здесь рассматривается совсем другой аспект. Возможно аспект неграмотный (с точки зрения тех кто хорошо разбирается в теме DI и давно его использует), но уж какой есть. Имхо нельзя игнорировать тот факт, что из за базза поднятого вокруг DI фреймворков их пытаются использовать не так и не там полностью игнорируя негативные результаты.

Вот в чем проблема и смысл топика, а вовсе не в том, что люди которым приходится отхреначивать DI из провальных проектов как-то не очень правильно понимают идеи DI.