Как стать автором
Обновить
2
0.2
Drone @dorne

Пользователь

Отправить сообщение
Ну то есть вы все равно где-то явно определили этот набор данных. Причем в потребителе зависимости (т.е., том месте, о котором зависимость знать не должна).

Да. Методы интерфейсов, выполнение которых подразумевает инжектирование данных (BuildTop) помечаеются аттрибутами говорящими «Я инжектирую это, это и это».
Интерфейсы компонентов, реализация которых подразумевает разрешение данных из контейнера (IRanker) помечаются аттрибутом «Я ожидаю данные этого, этого и этого типов из контейнера».

Тому, кто реализует IRanker не нужно знать кто эти данные инжектирует, но это всегда можно узнать по парным аттрибутам.
Погода на Марсе — тоже?

Нет. Только то что передано в качастве параметров методоу BuildTop
Каким контрактом?

Описанием метода BuildTop и соответствующими аттрибуттами на его параметрах, которые я, к сожалению, оставил, за рамками поста.
Юнит-тесты тут вообще не при чем, корректность вбрасывания проверяется интеграционными.

Корректность передачи контекста тоже интеграционными?
Я говорю про тесты на способность реализации метода BuildTop выполнять его функцию. Если теперь, из-за использования Data Injection они стали интеграционными, то ок.

По факту, это означает, что вы используете обычный контекст, просто извращенным способом.

Смысл называть это отдельным шаблоном?

Этот шаблон решает ту же проблему что и контекст, но на мой взгляд, делает это удобнее. Но решает он её иначе. Поэтому это отдельный шаблон.
Вот только вам надо не забыть вбросить данные в контейнер (не имея никакой конкретной информации о том, какие именно данные нужны потребителям)

Поэтому вбрасываются все данные которые есть. А потребители сами разбирутся, что им нужно. Благо список всего, что им можно получить четко обозначен контрактом.
при этом не забыть породить все потребители именно в этом контейнере.

Об этом трудно забыть. Т.к. компонент в этом случае не заработает в принципе, даже если данные не нужны никому. И не пройдет ни один юнит тест.

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

Да. Одновременно с этим ограничивая время жизни зависящих от контекста компонентов временем жизни дочернего контейнера (не больше), что позволяет использовать контексто-зависимые техники кэширования и т.п.
Но сразу после того как я инжектирую данные в контейнер я могу забыть об этом и, при использовании любого конктерного сконструированного IRanker, не думать более об этом при каждом вызове.
Более того, я поделил обязанности по передаче констекста и собственно ранжированию между разными классами.
Обычно для решения вашей проблемы банально вводят контекст операции.

Когда операция становится частью чего-то большего появляется контекст контекста, а потом контекст контекста контекста.
Алгоритм конкретного IRanker зависит от этого.

Да. Но мы хотим зависеть только от обстракции.

Вы все равно это передаете. Просто не в вызов, а в конструктор. Суть от этого не меняется.


Я данные не передаю. Я позволяю любой конкретной реализации получить эти данные, если ей это нужно. Происходит инверсия управления.
Да.
С точки зрения абстракции мне удобнее не знать, какие именно данные нужны конкретному IRanker чтобы выставить оценку. Это не имеет для алгоритма ранжирования никакого значения. Зачем же я должен передавать в качестве параместра всё что может понадобиться какой-то из реализацй, но скорее всего не понадобится?
Не для сокрытия ли этого как раз и нужен IoC?

Я всегда думал, что смысл DI не в конструировании объектов. С этим прекрасно справляются обычные фабрики. Я всегда думал, что DI нужен для того, чтобы позволить компоненту ничего не знать про то, от чего зависит компопент, от которого он сам зависит (я говорю про зависимости зависимостей).
Это аналогично принципу «вассал моего вассала — не мой вассал» и сильно упрощает жизнь.

Делегирование конструирования объектов на контейнер, это то, чем мы платим (да это цена, ибо это не очевидно) за избавление от проблемы зависимостей зависимостей, и многих других.
Они, так же как и плюсы Dependency Injection становятся отчетливее, когда зависимостей по данным становится очень много.
Передача лишнего параметра в данном случае видится мне аналогией конструирования объекта через new с передачей параметра. Это нормально, до тех пор, пока мы не ловим себя на том, что нам приходится разрешать параметры параметров и параметры параметров параметров, чтобы передать их в конструктор.
Я считаю, что понять Data Injection не сложнее, а плюсы тоже есть. Для меня они иногда (не всегда) перевешивают.
То же самое я думаю и я.
Когда-то при чтении кода вы впервые столкнулись с Dependency Injection, и, скорее всего, впервые столкнувшись с ним, вы на мгновение подумали, что это не просто и не очевидно.
Только в первый раз ;-)
при кривой конфигурации


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

А это (доступ ко внешним данным) кому-то нужно? А вот прозрачность системы вы резко понизили, что на архитектуре сказывается отрицательно.


Если доступа ко внешним данным никому не нужно, то я не рекомендую применять описаный шаблон. Даже запрещаю это делать.
Если же всё-таки он нужен, то компонент который инжектирует данные должен быть соответствующем образом отмечен, как и компонент, реализация которого допускает импорт данных из контейнера c явным указанием всех таких типов данных. Например, аттрибутами.
Аттрибутами на интерфейсе, на на которые есть четкая документация.
Есть ещё маркерные интерфейсы.
А ещё можно сделать так, чтобы для реализующего человека при одном взгляде на интерфейс, который он реализует стало понятно, что, лежащие в контейнере данные являются частью контракта, и на какие именно данные он в праве рассчитывать.
А сделать это очень просто…
Задача любого конкретного ранжировщика — выставить оценку команде. Какие данные нужны конкретному ранжировщику, это детали его реализации и к задаче реализации компонента не имеют. Это хорошая абстракция. Но, ранжировщикам потенциально могут потребоваться все данные, которые получает на вход в качестве параметров метод BuildTop. Применяя шаблон Data Dependency мы позволяем любой реализации ранжировщика получить доступ к этим данным, но не обязываем её это делать.

В случае, когда никому из ранжировщиков эти данные не нужны — Data Dependency применять не нужно.

Шаблон полезно иметь в арсенале на случай, если такое требование появится позже. В этом случае мы просто применим рефакторинг «Convert method to Method Object» и шаблон Data Dependency. Изменения интерфейса IRanker не потребуется. Таким образом, мы удовлетворим новое требование и сохраним совместимость с имеющимися реализациями.
И так я тоже решал эту задачу. С некоторыми оговорками:
«Композиция» реализовывала другой интерфейс, ибо результат подсчета композитного рейтинга не был целым числом. (В моем случае вообще не был числом).

В зависимости от используемого контейнера, может дополнительно понадобиться примитив поставщика оценщиков
Упомянутая возможность Unity мне хорошо известна и была использована в первую очередь. Проблема может возникнуть, если оценщики зависят друг от друга, именованые регистрации в Unity, к сожалению, друг о друге ничего не знают. Дефолтная реализация IRankerProvider делает именно то, что вы написали — разрешает массив. Но, сам интерфейс делается для того, чтобы поведеление можно было изменить. Можно ещё зарегистрировать InjectionFactory для массива. Как говорится, по желанию и никакого приведения типов.

BuilerWorker в даной ситуации больше похож на паттерн «MethodObject» и ничто не мешает его тестировать, при соответствующем изменении видимости.

Моим примером я избавила вас как минимум от двух не нужных компонентов:
закрытого и не тестируемого BuilerWorker а так же от IRankerProvider


Но вы не решили проблему, с которой я начал — как в ранжировщики передавать «опциональные» параметры.

Это оправдано по производительности, т.к. разрешение зависимостей делается единожды, а параметры передаются для каждой уникальной пары из ранжировщика и команды.
Это оправдано архитектурно, т.к. компоненты использующие DD могут использовать друг друга, а, следовательно, ранжировщики смогут получить доступ не только к данным, зарегистрированным в текушем контексте, но и к данным зарегистрированным во внешнем контексте по отношению к TopBuilder.
12 ...
33

Информация

В рейтинге
2 082-й
Откуда
Москва, Москва и Московская обл., Россия
Зарегистрирован
Активность