Здравствуйте, ·, Вы писали:
·>Здравствуйте, 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>>>>Это упрощённый вариант для объяснения того, что где-то в создании объектов может быть ошибка, которую всё равно не отловит компилятор.
V>>·>Именно. К этому я и клоню — такой код (wiring-код в том числе) никто в здравом уме не пишет и таких ошибок просто не будет.
V>>Ещё и не такой пишут, поверьте.
·>Верю, конечно. Но если в проекте пишут такой код, то проблемы с wiring-ом будут незаметны на фоне всего остального.
Пожалуй, да.
V>>>>Именно это я и хотел продемонстрировать. Эта ошибка может быть и в wiring коде и в регистрации объектов в контейнере, поэтому с этой точки зрения без разницы, использовать контейнер или нет.
V>>·>Как видишь — эта ошибка в wiring коде стала сразу явной и обнаруживаемой на раз, если не компилятором, то хоть статическим анализом. А если использовать контейнер — то просто навернётся на проде. Спасибо, что помог доказать мой тезис Почему ты не видишь эту разницу — неясно.
V>>Потому, что Вы продолжаете цепляться к конкретному коду и не хотите понимать идею. А идея в том, что инициализация может быть сложной, иметь вложенные вызовы, внешние зависимости и т.п. И в этой реализации могут быть ошибки. В том числе и не обнаруживаемые компилятором. Или обнаруживаемые, но не ошибкой, а warningом, которые индусы игнорируют.
·>Сложная инициализация wiring-кодом пишется и тестируется гораздо проще, чем на фреймворке. Но, повторюсь, на wiring код можно хотя бы натравить инструменты и найти ошибки-варнинги. С фреймворком будет говно и никак ни проанализировать, ни исправить, единственный надёжный тест — запустить на проде и поглядеть что поломалось.
К сожалению, инициализация в тестовом режиме не проверит инициализацию в проде. Просто потом, что это другая инициализация.
V>>>>Чтобы написать — не нужен. А чтобы хранить ссылку на эту фабрику для её использования из разных мест нужен либо библиотечный контейнер либо самописный.
V>>·>Где хранить? Зачем? Фабрика это по сути например Func<HttpRequest, RequestContext> и собственно всё.
V>>Ну как зачем? А как эту фабрику использовать? Эту Func<> ведь нужно как-то вызвать, правильно? А для этого присвоить переменной.
·>Чё? wiring-код ничего не вызывает, кроме конструкторов (ну ладно, ещё может билдеры сложных компонент). Вызовы логики происходят внутри связываемых компонент и ни в wiring-коде, ни в конфиге фреймворка их быть не должно. Т.е. в wiring-коде эту фабрику не надо использовать — её надо только создать и передать конструкторам других компонент.
ок, скажу совсем просто: нужно передать фабрику в 2 конструктора. Для этого её нужно сохранить в переменной, так ведь? Эта переменная и будет точкой хранения.
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БазаДанных>().
Понятно, меня не поняли. Забей на название, проще кодом.
Первый стиль (свой класс):
class Services
{
public IDatabase Database { get; set; }
public ILogger Logger { get; set; }
}
Использование:
var db = myServices.Database;
Второй стиль (готовая библиотека)
class Services
{
public T Get<T>();
}
Использование:
var db = myServices.Get<IDatabase>();
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"
Имелся в виду IoC контейнер. Я иногда ошибочно пишу DI контейнер