Хабр Курсы для всех
РЕКЛАМА
Практикум, Хекслет, SkyPro, авторские курсы — собрали всех и попросили скидки. Осталось выбрать!
Код, который пишется для того, чтобы прошел некоторый тест, по определению поддается тестированию. Более того, создается сильная мотивация для разбиения программы на модули, чтобы каждый модуль можно было тестировать независимо. Поэтому проект, разрабатываемый таким способом, оказывается существенно менее связанным.
IsUserOnline, и вот ему место в тесте. Кстати, если бы сократили код таким образом, то увидели бы, что у вас в первом тесте тоже есть этот сетап, а его там быть не должно.Таким образом, добиваясь тестируемости нашего кода, мы смогли параллельно улучшить архитектуру нашего приложения.
Приведите пожалуйста хоть один пример, почему высокая связанность может оказаться в итоге лучшим решением.
Сейчас мы переписываем проект, оказавшийся погубленным отсутствием этого самого dependency injection.
Но использовать повторно их никак не получится, потому что все модули так крепко связаны между собой, что перетащить можно только все целиком, но никак ни один кусочек.
Так, стоп. Давайте вы не будете подменять IoC на связность (высокую или низкую). IoC — это один из путей к низкой связности, но не единственный.
И отдельно не надо путать IoC и DI. Второе — частный случай первого, но не наоборот.
Сейчас мы переписываем проект, оказавшийся погубленным отсутствием этого самого dependency injection.нашли что то про IoC?
когда вы понимаете, что интерфейсы нужно оставлять так, чтобы они были видимы везде
Я ничего не говорил про IoC.
В его коде начинают появляться фабрики и IoC контейнер, а на столе книга gof про паттерны.[...] И, самое главное, все зависимости мы теперь можем подменить, передав в конструктор заглушки для теста.
Вопрос следующий, в каких случаях прямое взаимодействие между объектами лучше чем взаимодействие через интерфейс.
А вот это в корне не верное рассуждение.
Например, я, как любитель TDD пришел к выводу что он хорошо защищает от лишних сущностей, т.к. абстракция делается только для устранения дублирования
Зачем отделять ORM от вашего датафасада слоем для меня загадка.
Никакого собственного функционала, который можно протестировать в отрыве от ORM контейнера, у датафасада нет.
Ваш пример надуман.
Зато появился новый уровень абстрации, не обусловленный никакой семантикой.. Этот уровень абстрации усложняет код (удорожает рефакторинг, ...
Поэтому, мне кажется, уже давно пора признать: IoC в его нынешнем понимании и применении чаще всего необходим только для модульного тестирования.
Не улучшили. Просто сделали так, чтобы нам было удобно писать тесты. Я не хочу сказать, что это плохо, поскольку тестируемость — это тоже один из атрибутов архитектуры; я просто хочу, чтобы мы не лукавили, говоря, что «помимо тестов, мы улучшили еще и архитектуру».
Нет, он упрощает код и удешевляет рефакторинг, потому что теперь вместо одного толстого уровня можно независимо модифицировать и рефакторить два тонких, которые знают и делают принципиально меньше.
DI — наверно, единственный универсальный способ не только передать зависимости в модуль, но и отделить логику от конструирования
Еще как улучшили, так как увеличили ее гибкость.
Я предполагаю, что Вы просто не разделяете понятия «хорошая архитектура» и «простая архитектура».
Да и нет в сложности нет ничего плохого, если она контролируема.
Содержание и толщина слоев изменилась очень сильно. Хотя бы потому, что если слой занимается инстанцированием зависимостей, это плюс одна ответственность на каждую зависимость.
Где же здесь упрощение?
Сервис локатор и фабрику тоже надо как-то передать, и обычно это все равно DI, так что сути не меняет.
Хотя бы потому, что если слой занимается инстанцированием зависимостей, это плюс одна ответственность на каждую зависимость. Эта ответственность может быть очень непростой — например, нужно передать все необходимые параметры конструктору и обработать ошибки. И теперь Ваш код контролирует время жизни зависимости, она без него не существует, а он — без нее. Где же здесь упрощение?
Тесты — это малая часть того, что можно получить, используя IoC.
И первое допущение, которым оперируют люди, пропагандирующие IoC — это то, что нам когда-нибудь понадобится заменить одну реализацию на другую. Так вот, это допущение верно для не такого уж большого количества проектов
я просто хочу, чтобы мы не лукавили, говоря, что «помимо тестов, мы улучшили еще и архитектуру». Не улучшили. Просто сделали так, чтобы нам было удобно писать тесты.
юнит тесты это не только инструмент борьбы с регрессией в коде, но также и отличная инвестиция в качественную архитектуру
Плюс бесплатный бонус — простота расширения/изменения по некоторым направлениям.
Архитектура стала лучше как минимум по двум из параметров (тестируемость, расширяемость)
не став (допущение) хуже по другим.
Бонус бесплатен — целью изменения архитектуры было облегчении тестируемости, а не расширяемости.
В общем же случае, считаю что вынесение зависимостей систему не усложняет, т. к. количество сущностей в ней не увеличивается, а изменяется только места их инстанцирования и т. п.
Вот именно поэтому расширяемость еще не достигнута. Есть только возможность подмены реализации, что совсем не одно и то же.
А вот введение дополнительных абстракций (а именно оно показано в примере к статье) — усложняет.
IoC в его нынешнем понимании и применении чаще всего необходим только для модульного тестирования.
Из практики, IoC контейнер нужен в основном для легкого пробрасывание зависимостей.
DI CONTAINERS are also known as Inversion of Control (IoC) Containers or (more rarely) Lightweight Containers.Mark Seeman
Я вам написал, что ваша практика не равна всеобщей, приведя пример из своей, когда IoC применяется в основном не для тестирования, а для сборки графа объектов.
Однако: «IoC в нынешнем применении»(с) = DI/IoC контейнер в 99% случаев.
А IoC (inversion of control, без слова «контейнер») — это методология, при которой каждый объект использует не конкретные зависимости, а их абстракции (например, интерфейс вместо конкретного класса).
Черт с ней с тестируемостью, но ваше решение не очень хорошее с т.з. читаемости — нельзя сразу понять какие у класса внешние зависимости.
невозможно определить класс как sealed и необходимость пометки методов как virtual.
обязанность разработчика подкласса писать его так, чтобы он не нарушал LSP
А зачем? Вот опять-таки, если мы не говорим о модульности (нет требований) и не говорим о тестируемости — то зачем нам видеть внешние зависимости?
Если бы все люди исполняли свои обязанности, жизнь была бы прекрасна. Однако даже в одной команде при большой базе кода это малореализуемо (к сожалению), а уж при взаимодействии нескольких команд… В итоге, в каждом месте, где есть возможность расширения, появляется необходимость обработки всех пограничных ситуаций.
Поиск идеальной архитектуры — бесполезное занятие.
Юнит тесты — это отличная проверка вашей архитектуры на низкую связанность между модулями. Но всегда ли это необходимо конкретному приложению? На этот вопрос правильного ответа нет. Проектирование сложных технических систем — это всегда поиск компромисса. Идеальной архитектуры не бывает, так как учесть все возможные сценарии развития приложения при проектировании невозможно. Качество архитектуры зависит от множества параметров, часто друг друга взаимоисключающих. Есть старая шутка, что любую проблему дизайна можно решить путём введения дополнительного уровня абстракции, кроме проблемы слишком большого количества уровней абстракций. Поэтому не стоит рассматривать как догму, что взаимодействие между объектами должно быть построено только на основе интерфейсов, главное чтобы выбор, совершенный вами, был осознанным и вы понимали, что код, допускающий взаимодействие между реализациями, становится менее гибким и, как следствие, теряет возможность быть протестированным модульными тестами.
Почему использование юнит тестов это отличная инвестиция в качественную архитектуру