All streams
Search
Write a publication
Pull to refresh

Comments 20

Добавьте в статью хотя бы минимальные примеры, каким образом Ambient Context является IoC? Потому что к DI я думаю все привыкли, а вот с Ambient Context у меня прямо сомнения, что это к IoC как то относится.

Попытался спросить у чатгпт, какой будет типовой пример Ambient Context - он мне нарисовал синглтон, что звучит прямо очень грустно и плохо. Это не инверсия зависимостей, а скрытие зависимостей - мы зависим от синглтона, но не заявляем этого явно.

Некоторые наши коллеги вообще считают Ambient Context антипаттерном, но я не стал этого выносить в текст публикации, чтобы не сильно деформировать мнения при опросе.

Я наверное мог бы согласиться, что это антипаттерн. В 9 случаях из 10 лучше сделать DI - меньше шансов ошибиться. Да, есть сценарии где "контекст" выполнения полезен, я даже писал несколько раз такие вещи, но в целом это такая "магия" о которой нужно знать, чтобы не совершать ошибок.

А как ещё называть сервис локатор, который возвращает значение в зависимости от фазы луны?

Не настолько плохо всё таки. Условный HttpContext в дотнет как раз Ambient Context. Thread.CurrentPrincipal - тоже по идее. Т.е. в c# разница между ними есть, но обычно это прямо конкретные фичи реализованные в виде контекста "действия" и о них надо знать (и примерно понимать как они работают).

Насчёт того, что это контекст - согласен. Насчёт того, что это IoC - нет. Суть IoC - в передаче управления созданием объектов в программе в отдельное место (Composition Root). Различные части программы не создают сами нужные им зависимости, а получают их тем (инъекция) или иным способом (сервис-локатор) из этого места.

В случае с HttpContext и Thread.CurrentPrincipal я полагаю, что подобные объекты создаются не централизовано, а в местах обработки соответствующих запросов, после чего передаются в качестве параметра или свойства объекта.

Да, это похоже на Ambient Context, но не хватает глобальности, чтобы им быть. Два разных объекта HttpContext не пересекаются по своим свойствам и могут существовать в приложении одновременно (в nodejs могли бы).

По сути, коллега @Deosis прав: Ambietn Context - это такой Сервис Локатор, через который части программы имеют доступ к Composition Root (или к хранилищу результатов его работы). А если предположить, что в этот контекст может писать не только Composition Root, а вообще любая часть программы, то он прав и во второй части - "возвращает значение в зависимости от фазы луны".

Не обязано быть общим для всех, и многие дотнет разработчики как раз пишут про потоко-зависимые реализации (нынче уже с AsyncLocal, т.е. скорее про поток выполнения речь), например https://blog.ploeh.dk/2019/01/21/some-thoughts-on-anti-patterns/

Но в целом да, не любят в дотнете такой подход.

Лично я использую аналог HttpContext"а для передачи объектов между обработчиками (контроллерами) - положил, взял. Но можно для каждого потока обработки инициировать пустой Composition Root (контейнер объектов или что там аналогичное для AC?), (не)связанный с родительским Composition Root и это уже будет IoC с возможностью создания новых объектов при необходимости. Тем не менее, возможность порождения объектов в таком контексте всё-таки должна быть, чтобы считаться IoC. Без такой возможности HttpContext слабо отличается по своим свойствам от какого-нибудь in-memory storage.

Так что - да, локальный, потоковый Composition Root может иметь место в IoC.

Так что - да, локальный, потоковый Composition Root может иметь место в IoC.

Переспал я с этой мыслью и пришёл к следующему. Я разделяю у себя код на две большие группы: DTO и обработчики. DTO порождаются через фабрики (обработчики). Обработчики зависят от других обработчиков. Некоторые обработчики не имеют своего собственного внутреннего состояния для сохранения результатов работы (обработчики-функции), некоторые имеют (обработчики-объекты). Первые принимают на вход набор параметров, обрабатывают их (в том числе и с использованием обработчиков-зависимостей) и выдают результат. Вторые делают то же самое, но при этом изменяют своё внутреннее состояние (типичный пример - кэширование результатов обработки).

Так вот, когда у нас единый Composition Root у нас все обработчики-функции, зависящие от других обработчиков-функций, будут в приложении иметь жизненный цикл синглтон. Т.е., нам достаточно иметь по одному экземпляру таких обработчиков на всё приложение. Данные (в виде DTO) независимо двигаются по иерархии обработчиков и возвращаются в виде результата, нигде не оставляя следов.

В случае с обработчиками-объектами всё сложнее. Если это объект уровня приложения (например, кэширует в памяти обращения к файловой системе), то он тоже синглтон, а если уровня, например, обработки отдельного HTTP-запроса (потока вычислений), то это уже инстанс (порождается на поток).

Проблема в том, что набор зависимостей в конкретном обработчике (функции или объекте) - это по сути тоже состояние. Если все зависимости - функции, то все могут быть синглтонами. Но как только какая-то из зависимостей является объектом потокового уровня, то вся иерархия обработчиков, строящихся на этой зависимости должна пересоздаваться для каждого потока обработки (HttpContext <= Model <= Operation <= Service <= ...).

JavaScript асинхронный и однопоточный (если не считать worker'ов) и в рамках этого одного потока выполнения программы я не вижу особого смысла пересоздавать иерархию обработчиков на каждый поток обработки (HTTP-)запроса. Разумнее передавать обработчикам (функциям и объектам уровня приложения) контекст в качестве параметра (пусть это и не DTO, а FTO).

В worker'ах же Composition Root отдельный и независимый от Composition Root основного приложения. Таким образом, Composition Root всё равно должен быть один на поток выполнения (thread, worker) и никакой иерархии Composition Root'ов я там не вижу.

Т.е., вот здесь:

> (не)связанный с родительским Composition Root

нужно без скобочек: несвязанный. Я в курсе, что есть реализации, позволяющие строить иерархии Composition Roots, но в своей парадигме разработки ПО я считаю это ненужным.

В статье, с которой автор сего поста спорит не читая (ещё и кичится этим), всё подробно описано.

А, я не заметил, что речь идёт о $mol и js. Оценивал с точки зрения c# разработчика.

Статью почитал, но без знания js непонятно, в чём проблема и как она решается. По второй ссылке кажется что это просто запись всего в корень, как GOD-объект, но опять таки, понятия не имею что там происходит.

Там дерево объектов наследует контекст по иерархии владения с возможностью его переопределения для контролируемых объектов. Сами контексты образуют иерархию наследования в корне которой находится дефолтный контекст, куда попадают все глобальные сущности в момент своего объявления автоматически. Тесты запускаются по умолчанию в изолированном контексте, где замоканы все модули, что общаются со внешним миром. Но можно запускать их с разным уровнями изоляции вплоть до "проверяем на проде без изоляции, что всё работает как ожидается". Всё это тайпчекается, лениво инициализируется и автоматически разрушается, что даёт и высокую скорость работы, экономию ресурсов и превентивную защиту от ошибок.

Звучит удобно, но без знания js большего не могу получить из текста.

В C# есть несколько механизмов со схожим поведением (AsyncLocal например), но они и нужны в довольно редких случаях.

Я уверен, что принципиальные отличия одного метода от другого не зависят от особенностей их реализации. Первый раз я вашу статью я просматривал довольно давно ("перевёрнутую" машинку помню) и могу ответственно заявить: то, что там описано - это не DI.

Вам удобнее AC, мне - DI. Задачи они решают примерно одинаковые (IoC). Вы заявили, что "В хороших фреймворках IoC реализуют не через инъекцию, а неявно через контекст окружения".

Знаете, я и сам своего рода автор фреймворка
Знаете, я и сам своего рода автор фреймворка

У меня, конечно, есть некоторая профессиональная деформация - я так долго использовал DI в Java & PHP, что пришлось даже написать свой собственный контейнер, чтобы лично мне было удобно кодировать на JS. Но у меня нет и десятой доли вашей профессиональной самоуверенности, время от времени переходящей в наглость.

Заявлять же, что "в хороших фреймворках IoC реализуют неявно" - это наглость, переходящая в непрофессионализм. Ваша реализация IoC в вашем фреймворке размещает всё в globals и делает каждый объект доступным другим объектам. Я уже показал, как убивается ваш сайт с консоли браузера? Это же может сделать любой JS-код на странице.

Вы написали хороший фреймворк для себя и своих друзей, которым доверяете. Отлично, пользуйтесь им. Но не стоит при этом чохом хаять другие фреймворки.

Зашёл в статью посмотреть КДПВ, а КДПВ пропала... Безобразие!

У-упс... похоже, я её не поставил в саму статью. Исправил.

Автор, почему так важно что скажет сообщество? Ну, например, представим, что вы во всем не правы. Вас это успокоит? Вы готовы перелопатить ваш продакшн-рэди код? Самые языкастые могут доказать какую угодно теорию. Но вы же практик. Зачем вам все это?

Так ведь опрос - это и есть практика, разве нет? Важно не то, "что скажет сообщество", важно то, какой процент ответивших использует IoC, какой из методов более популярен и насколько. Я так познаЮ мир. Например, после опроса по поводу используемой версии HTTP я пришёл к выводу, что значимость HTTP/2 мной довольно сильно преувеличена.

Сообщество могло вообще сказать, что ему всё равно, какой из двух методов IoC используется, потому что оно вообще не видит смысла в использовании IoC. Могло разделиться пополам между двумя методами. Могло перевесить в сторону AC. Каждый из этих результатов скорректировал бы моё дальнейшее движение. Нет, я бы не перелопатил свой код - слишком большая инерция у того, что уже есть. Но я бы точно изменил границы применимости своих решений.

К тому же периодическая ревизия основ помогает видеть новые направления для развития. Например, в процессе наших прений с коллегой Карловским мне пришла в голову мысль попробовать добавить в свой контейнер возможность "замораживания" создаваемых объектов by default. Но эту мысль ещё нужно хорошо подумать, есть у неё конфликты с тем, что уже есть.

Ну, например, представим, что вы во всем не правы.

Если мне докажут, что я во всём не прав, я всё буду делать по-другому. Какой смысл делать неправильно, верно?

Вот хороший рассказ про программистов. Надо лишь сделать поправку, что Грин был романтиком, поэтому ограничился только двумя итерациями и в конце дописал получение выигрыша. В реале же итераций гораздо больше и никакой гарантии выигрыша.

Поэтому вопрос "зачем?" чертовски занимателен.

Sign up to leave a comment.

Articles