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