Блог :. Делаем свой DI-контейнер вместо Swinject

Делаем свой DI-контейнер вместо Swinject

26 Сен, 2019

Последнее время стало модно использовать всеми горячо любимый фреймворк Swinject для внедрения зависимостей , о чем не стесняются писать в резюме. Для чего? Если коротко, то ваш код не должен быть сильно связным, все зависимости каждого компонента должны зависеть лишь от абстракций, а не от конкретной реализации. По крайней мере этого просит буква D из принципов SOLID. А внедрять эти зависимости можно из так называемого контейнера для внедрения зависимостей, напоминающий фабрику. Этот контейнер возвращает определенный инстанс определенного класса, отвечающему заданному протоколу, в том числе и с другими вложенными зависимостями. Получается, что пользователь не знает, каким образом и какой конкретный объект ему был возвращен контейнером, т.к. работает с объектом строго через заданный протокол. Таким образом можно еще и удобно протестировать работу отдельного компонента, сделав контейнер с заданной конфигурацией, в которой будут отдаваться классы-заглушки с законсервированными данными для зависимостей тестируемого компонента. Еще прикольно, что Swinject умеет задавать стратегию создания объекта, например, либо каждый раз новый объект, либо один раз в виде синглтона (+ это еще не все)

Это все прекрасно, но при каждом обновлении Xcode я получал ошибку:

Module compiled with Swift 4.2 cannot be imported by the Swift 5.0 compiler

Это означает, что настала пора снова брать Swinject и зачем-то пересобирать, затем обратно класть обновленный swiftmodule в проект. Это как-то не очень. После обновления до Swift 5.1 я его выбросил нахрен и написал свой Dependency Injection Container, причем ровно с таким же интерфейсом как и у Swinject, даже переписывать ничего не пришлось в уже существовавшем контейнере. Вроде бы дело нехитрое, запомнить стратегию создания объекта для заданного протокола в кложуре и когда тебя попросят - просто зарезолвить. Кроме того, добавилась еще и потокобезопасность! Да-да, в Swinject все разваливалось при резолвинге из разных потоков. Ну и еще я поддержал стратегию создания объектов в виде синглтона или каждый раз нового. Больше мне ничего от огромного фреймворка не нужно было.  Вышел компактный класс, который живет в хелперах проекта, можно забыть про отдельную перекомпиляцию рядом лежащего модуля. Ну и опять же, в очередной раз избавились от зависимости от модуля внедрения зависимостей, ведь когда-нибудь это могло бы сыграть злую шутку при очередном обновлении языка Swift