Здравствуйте, vmpire, Вы писали:
V>·>Как раз зависит. Когда ты пишешь wiring code на самом ЯП — у тебя есть вся выразительная сила ЯП. Хочешь интерфейсы, хочешь классы, хочешь if/switch/циклы/методы/лямбды — полный арсенал. А с фреймворком — только полтора типичных паттерна в ·>которые умеет фреймворк, и целая наука каких-то плагинов, магических расширений и аннотаций, и это всё может понадобиться чтобы реализовать какой-нибудь банальный switch.
V>В данном подвопросе было не про гибкость, а про то, что в обоих вариантах можно забыть что-нибудь инициализировать и будет падение в рантайме.
А я говорю о том, что с вариантом wiring кода можно написать код так, что забыть будет невозможно и падения в рантайме произойти не может. А в случае фреймворка — код написать нормально нельзя никак. В этом и есть принципиальная разница.
V>·>Простейший вариант это new IndexPageController(new ProfileService(new ProfileRepository())). Даже класс и поля необязательны.
V>Результат этого выражения нужно где-то хранить. Вот для этого поля и классы.
Ну как минимум — в локальной переменной или тупо однострочник
void main(String args[]){new App(new Dependency()).start(args);}.
V>·>Это как-то не очень подходит под общепринятое понятие IoC-контейнер.
V>Специально попытался найти официальное определение — не нашёл.
Это понятие родилось где-то в Spring, стоит читать его доку.
V>Поэтому я понимаю буквально: IoC-контейнер — контейнер, хранящий объекты, используемые для реализации IoC
Ну типа того. А wiring код может ничего не хранить. Он просто соединяет компоненты.
V>>>Это упрощённый вариант для объяснения того, что где-то в создании объектов может быть ошибка, которую всё равно не отловит компилятор.
V>·>Именно. К этому я и клоню — такой код (wiring-код в том числе) никто в здравом уме не пишет и таких ошибок просто не будет.
V>Ещё и не такой пишут, поверьте.
Верю, конечно. Но если в проекте пишут такой код, то проблемы с wiring-ом будут незаметны на фоне всего остального.
V>>>Именно это я и хотел продемонстрировать. Эта ошибка может быть и в wiring коде и в регистрации объектов в контейнере, поэтому с этой точки зрения без разницы, использовать контейнер или нет.
V>·>Как видишь — эта ошибка в wiring коде стала сразу явной и обнаруживаемой на раз, если не компилятором, то хоть статическим анализом. А если использовать контейнер — то просто навернётся на проде. Спасибо, что помог доказать мой тезис Почему ты не видишь эту разницу — неясно.
V>Потому, что Вы продолжаете цепляться к конкретному коду и не хотите понимать идею. А идея в том, что инициализация может быть сложной, иметь вложенные вызовы, внешние зависимости и т.п. И в этой реализации могут быть ошибки. В том числе и не обнаруживаемые компилятором. Или обнаруживаемые, но не ошибкой, а warningом, которые индусы игнорируют.
Сложная инициализация wiring-кодом пишется и тестируется гораздо проще, чем на фреймворке. Но, повторюсь, на wiring код можно хотя бы натравить инструменты и найти ошибки-варнинги. С фреймворком будет говно и никак ни проанализировать, ни исправить, единственный надёжный тест — запустить на проде и поглядеть что поломалось.
V>>>Чтобы написать — не нужен. А чтобы хранить ссылку на эту фабрику для её использования из разных мест нужен либо библиотечный контейнер либо самописный.
V>·>Где хранить? Зачем? Фабрика это по сути например Func<HttpRequest, RequestContext> и собственно всё.
V>Ну как зачем? А как эту фабрику использовать? Эту Func<> ведь нужно как-то вызвать, правильно? А для этого присвоить переменной.
Чё? wiring-код ничего не вызывает, кроме конструкторов (ну ладно, ещё может билдеры сложных компонент). Вызовы логики происходят внутри связываемых компонент и ни в wiring-коде, ни в конфиге фреймворка их быть не должно. Т.е. в wiring-коде эту фабрику не надо использовать — её надо только создать и передать конструкторам других компонент.
V>>>По сути контейнер есть ни что иное как глобальная переменная. Весь вопрос в её типизации и формате использования.
V>·>О. Спасибо, что напомнил. Знатный антипаттерн — глобальная переменная. А здесь даже публичная глобальная переменная свалка.
V>Вот с этим я согласен. Когда в контейнер начинают валить всё подряд — получается свалка.
А ты видел когда-то что-то другое? Контекст контейнера это огромный словарь всего подряд со всего приложения, и всё публично — что угодно может зависеть от чего угодно, т.к. всё в одной свалке. Возможности структуризации контекста практически отсутствуют.
V>>>Если хотите её использовать в стиле "дай мне ссылку на текущюю реализацию базы данных" — тогда можно использовать просто свой класс. Плюс — наличие переменной проверяется компилятором, в простых случаях проверяется и её инициализация.
V>>>Если хотите её использовать в стиле "дай мне ссылку на реализацию интерфейса IБазаДанных" — тогда удобнее использовать словарь или библиотечный контейнер. Плюс — проще реализовать различные времена жизни.
V>·>Ты опять запутался. Это уже не DI, а печально известный Service Locator — ещё более знатный и злой антипаттерн. И вот это всё говно в комплекте с любым фреймворком. Закопать надо давно.
V>Это ровно то (второй пример), чем занимается DI фреймворк.
Да боже. Не "DI фреймворк", а "IoC фреймворк". SL и DI это два противоположных подхода реализации IoC принципа. Не знаю что ты имеешь в виду, но в этом топике примеров кода SL не было.
"дай мне ссылку на реализацию интерфейса IБазаДанных" — это SL:
var db = serviceLocator.GetService<IБазаДанных>().
V>>>Если писать свою программу без всяких фреймворков, то это просто вопрос личных предпочтений.
V>>>Если в рамках готового фреймворка, который уже основан на DI, то разумно этот стиль и поддерживать.
V>·>wiring code — основан на DI тоже.
V>Это не важно, я про фреймворк. Если он внутри использует DI контейнер, то довольно сложно избежать его использования.
Я не знаю что такое DI-контейнер. На всякий случай: "dependency injection is a programming technique in which an object or function receives other objects or functions that it requires, as opposed to creating them internally"