Здравствуйте, Tom, Вы писали:
Tom>·>SL. В подавляющем числе случаев он не нужен. Проблема — он делает зависимости неявными.
Tom>? Что такое SL?
Service Locator.
Tom>Абсолютно согласен, зло злейшее. В 99% только Constructor Injection. Правда есть пару случаев когда из за "чужёго кривого дизайна" приходится использовать property injection но это исключение.
Но вот добрый контейнер легко и непринуждённо позволяет это пихать куда угодно в любых количествах.
Tom>Да конечно надо как то регистрации отличать друг от друга и ничего проще строк тут нету. Что бы эти строки перестали быть магическими — вынеси их в константу и используй эту константу как при регистарции так и при резолве.
А зачем тебе их по строкам отличать? Зачем их вообще отличать? Есть две ситуации.
Первая cитуация, у тебя действительно два инстанса и ты разные инстансы используешь в разных частях программы с разными целями. Ну например, DbConnection для основной БД и для отдельной БД для аудита, например. В этом случае тебе строки не нужны. В случае ручного связывания у тебя вместо строк это будет именем поля или локальной переменной.
Вторая ситуация, у тебя действительно несколько инстансов и их нужно как-то определять в рантайме в зависимости от каких-то данных. Ну например, найти инстанс какого-нибудь класса по фрагменту URL. В этом случае следует использовать не "ничего проще строк нету", а соответсвующий бизнес-требованиям тип — UrlFragment, enum, етс. Пихать везде строки — плохой стиль.
Tom>·>Сканирование сборок. В подавляющем числе случаев он не нужен. Доставляет столько удовольствия исследовать что куда залетело и почему. Притом это можно исследовать только на запущенной системе в данном (часто prod) окружении.
Tom>Ничего не понимаю что куда залетело. В общем случае фича которая называется auto wire работает прекрасно. Регистрируешь только то что отличается от принятых в контейнере конвенций.
Я о
http://www.lightinject.net/#assembly-scanning , а ты о чём?
Tom>·>Циклические зависимости. С DI+CI сделать цикл невозможно. Заставляет аккуратнее подходить к архитектуре приложения, а не всякие lazy/PI, которые тебе циклы создадут на ура.
Tom>Эммм а где антипаттерн? И причём тут контейнеры с DI. Если у тебя циклические зависимости то это проблема не контейнера а проблема твоего дизайна. Но даже в этом случае контейнер может подставить тебе плечо, ты можешь резолвить Lazy<T> и фактически резолв будет сделан в момент использования.
Циклические зависимости делать не надо. Никогда. А если это действительно неизбежно — это должно быть сложно и сразу явно видно. Ты можешь случайно добавить незаметив, а контейнер тебе всё сам свяжет и не пикнет, втихую добавив +100 к техническому долгу. А потом эту вязанку развязывать.
Tom>На стадии проектирования интерфейса и своей реализации интерфейса ты НЕ знаешь будут ли другие реализации которым может потребоваться очистка ресурсов. Это основная идея. В случае исполдьзования контейнера, обычно, на запрос создаётся так называемый child container который разрушается при окончании обработки запроса и который при разрушении вызовет Dispose у обьектов которые были в нём зарезолвлены. Причём он вызовет Dispose естественно вне зависимости от того унаследован интерфейс от IDsiposable или нет. В случае когда у тебя контейнера нет тебе надо либо самому писать такой функционал но это не возможно так как без контейнера нет единственного класса который контролирует время жизни всех обьектов. Либо наследовать интерфейс от IDisposable в тех местах где ты предпологаешь в реализациях может понадобится очистка ресурсов, но это жутко криво ибо во первых ты по сути не должен знать и не знаешь где очистка может понадобится а во вторых сам факт наследования от IDisposable это ад адский.
public class RequestModule и пусть он и управляет временем жизни создаваемых им IDisposable-ы ровно так как надо, а не универсальный всемогутер, который делает какую-то неявную магию.
Tom>>>Контейнеры много чего могут но это бесполезно обьяснять человеку у которого в голове установка что они зло.
Tom>·>Но это всё не надо использовать. Контейнеры можно использовать для каких-то сложных случаев, только в довольно маленьком чётко отделённом контексте, в плагинном менеджере каком-нибудь например. Но пихать везде — антипаттерн.
Tom>Так, то что это вдруг антипатотерн — это придумал ты. Основная масса современных разработчиков с этим не согласна
Я знаю. Сам таким был.
Потом попал в компанию, где история развития была такая: "что попало как попало" -> "монстр Spring Framework" -> "модный Guice" -> "DI, plain Java code". По началу тоже возмущался отстутсвием "современного" контейнера, а потом осенило.
Tom>·>Нет, просто пишешь классы-модули, которые делают wiring тестируемых поддеревьев зависимостей. И тестируешь именно их. Иначе интеграционные тесты тестируют тестовый wiring.
Tom>О чём и речь, без контейнера тебе надо собирать деревья зависимостей руками. Зачем это делать если это умеет делать контейнер.
Дерево зависимостей довольно сложная, но важная вещь. Оно не только является некого рода документом, описывающим высокоуровневую архитектуру, но и сдерживающим фактором говнокодинга. Если у тебя это самое дерево руками получается делать плохо, слишком сложно, непонятно, значит ты делаешь что-то не то, остановись, подумай — сделай проще. А контейнер эту всю сложность прячет под коврик и через какое-то время кроме как автоматически это дерево не строится. Контроль над сложностью потерян, значит без бутылки человек уже не разберётся что куда откуда.