Есть много проектов типа Simple Injector для различных языков программирования, позволяющих по имени класса, интерфейса или неймспейса, а иногда и папки зарегистрировать класс или всю группу классов, объединенных этим признаком, в каком-то регистре. Это делается для цели автоматического инстанцирования объекта без явного указания его зависимостей. Такую регистрацию группы объектов по общему признаку в регистре с целью дальнейшего инстанцирования я называю автоматической регистрацией зависимостей.
Если вы явно инстанцируете класс при его регистрации в регистре зависимостей, то это я не отношу к теме этой статьи.
А что в этом плохого?
В этом посте я расскажу о моем недовольстве, возникшем при работе с проектами, в которых применяется автоматическая регистрация зависимостей. Давайте по порядку.
Runtime vs compile time
Используя инструмент автоматической регистрации зависимостей, мы переносим проверку полноты зарегистрированных зависимостей в runtime. То есть, если на момент запуска программы у нас не окажется имплементации интерфейса, нужного для выполнения кода, мы узнаем об это в лучшем случае на старте приложения, а в худшем — уже при эксплуатации системы и от пользователей.
Вот пример. Если в проекте регистрация каких-то классов произошла, скажем, по их неймспейсу, а вы в какой-то момент переместили класс из него, то о проблеме вы узнаете в runtime. То же самое произойдет и при автоматической регистрации по интерфейсу, с той лишь разницей, что перемещение проблемой не будет, но вот снятие интерфейса приведет к проблеме, о которой вы узнаете после запуска приложения.
Рефакторинг кратно усложняется
Не только рефакторинг, но и изучение кода становится занятием весьма затруднительным. Всё потому, что при использовании автоматической регистрации зависимостей теряется доступ к современным возможностям сред разработки, которые позволили бы нам в один клик (find reference) узнать, кто использует этот класс, и ответить на следующие вопросы:
- Могу ли я удалить этот класс?
- Могу ли я перенести этот класс в другой namespace/папку?
и прочее.
В сумме с тем, что мы лишены возможности проверить полноту необходимых зависимостей при компиляции, любой рефакторинг — это упование на то, что у нас есть runtime-механизм в виде тестов, верификаций деревьев зависимости и подобного. И надежда, что он сработает. Следует отметить, что эти механизмы не только не гарантируют на 100%, что композиция кода корректна, но и значительно медленнее, чем компиляция.
Сокрытие реальной сложности системы
При ручном инстанцировании классов и всех их зависимостей вас может неслабо напугать монструозность файла, в котором всё это действо будет происходить. Смотря на тысячи строк кода инстанцирования классов и сравнивая их с десятком при использовании автоматической регистрации зависимостей, очень хочется поддаться искушению и перейти на «темную сторону».
Но о чем нам говорят эти тысячи строк кода инстанцирования объектов? О том, что этот модуль сложный, большой и нам следует подумать или о его структурировании через выделение подмодулей (которые сами явно инстанцирует свои зависимости), или над разделением этого модуля на несколько.
Это может показаться абстрактным, но убирание с глаз таких неудобных моментов как инстанцирование сотен объектов приводит к тому, что размер проекта неконтролируемо растет, и в итоге мы пропускаем момент, когда его следовало бы разделить на несколько проектов.
Лишние зависимости
Регистрируя зависимости и не контролируя их использование, мы рано или поздно приходим к ситуации, когда при старте приложения регистрируется значительно больше классов, чем реально требуется. Простое добавление в папку или имплементация интерфейса может привести к тому, что этот класс будет зарегистрирован в общем регистре.
Summary
Можно ли жить со всеми этими минусами и не замечать их? Конечно! Но я считаю, что это вырабатывает неправильные привычки разработки. Дело в том, что компиляция и типизация кода — это инструменты проверки корректности системы. Отказываясь от них, мы приходим к тому, что система становится хрупкой, поэтому разработчики боятся что-либо в ней менять. А страх разработчиков менять код уже приводит к тому, что качество кодовой базы деградирует, после чего внесение изменений в такой код становится процессом дорогостоящим.
Что хорошего в автоматической регистрации зависимостей?
А и правда, что хорошего? Предлагаю в комментариях подискутировать, почему же этот метод набрал популярность. Наверняка же у него есть поклонники среди читателей.
welovelain
Так это же проблема сред разработки.
Я думаю, при таком повороте событий отследить неконтролируемый размер проекта можно не только по мануально объявленным зависимостям.
Странно обвинять инструмент в самой его сути. Он же именно это и должен делать, если кто-то неправильно гвозди забивает, то это не молоток виноват.
chizh_andrey Автор
Да это так, но на сегодняшний день мне не известны среды в полной мере подержали тот инструментарий который доступен при явном объявление зависимостей.
Поделитесь своим опытом как вы контролируете рост сложности в проекте, мы ищем все возможности для сдерживания.
Я бы не стал называть это обвинением. Есть опасные инструменты, а есть менее опасные, просто нужно знать о возможных последствиях. Для нового разработчика на проекте нет ни одной подсказки о том что какая-то папка в проекте отличается от других и если в нее положить или убрать класс то поведение системы изменится. Он может об этом никогда и не узнать, т.к. баг упадет соседней команде через неделю после деплоя.
rjhdby
Если бы это было стандартной фичей языка, то да. Но обычно это вагон и маленькая тележка библиотек разной степени рефлексивности например.