Да.
С точки зрения абстракции мне удобнее не знать, какие именно данные нужны конкретному IRanker чтобы выставить оценку. Это не имеет для алгоритма ранжирования никакого значения. Зачем же я должен передавать в качестве параместра всё что может понадобиться какой-то из реализацй, но скорее всего не понадобится?
Не для сокрытия ли этого как раз и нужен IoC?
Я всегда думал, что смысл DI не в конструировании объектов. С этим прекрасно справляются обычные фабрики. Я всегда думал, что DI нужен для того, чтобы позволить компоненту ничего не знать про то, от чего зависит компопент, от которого он сам зависит (я говорю про зависимости зависимостей).
Это аналогично принципу «вассал моего вассала — не мой вассал» и сильно упрощает жизнь.
Делегирование конструирования объектов на контейнер, это то, чем мы платим (да это цена, ибо это не очевидно) за избавление от проблемы зависимостей зависимостей, и многих других.
Они, так же как и плюсы Dependency Injection становятся отчетливее, когда зависимостей по данным становится очень много.
Передача лишнего параметра в данном случае видится мне аналогией конструирования объекта через new с передачей параметра. Это нормально, до тех пор, пока мы не ловим себя на том, что нам приходится разрешать параметры параметров и параметры параметров параметров, чтобы передать их в конструктор.
Когда-то при чтении кода вы впервые столкнулись с 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.
Когда операция становится частью чего-то большего появляется контекст контекста, а потом контекст контекста контекста.
Да. Но мы хотим зависеть только от обстракции.
Я данные
не передаю
. Япозволяю
любой конкретной реализации получить эти данные,если ей это нужно
. Происходит инверсия управления.С точки зрения абстракции мне удобнее не знать, какие именно данные нужны конкретному
IRanker
чтобы выставить оценку. Это не имеет для алгоритма ранжирования никакого значения. Зачем же я должен передавать в качестве параместра всё что может понадобиться какой-то из реализацй, но скорее всего не понадобится?Не для сокрытия ли этого как раз и нужен IoC?
Это аналогично принципу «вассал моего вассала — не мой вассал» и сильно упрощает жизнь.
Делегирование конструирования объектов на контейнер, это то, чем мы платим (да это цена, ибо это не очевидно) за избавление от проблемы зависимостей зависимостей, и многих других.
Передача лишнего параметра в данном случае видится мне аналогией конструирования объекта через new с передачей параметра. Это нормально, до тех пор, пока мы не ловим себя на том, что нам приходится разрешать параметры параметров и параметры параметров параметров, чтобы передать их в конструктор.
Да, при кривой конфигурации в самом деле нет никакого выигрыша. Но в этом случае думать в первую очередь надо о том, что в проекте ещё кривое, кроме конфигурации. (Тот факт, что выигрыша не будет, если кто-то где-то ошибется не означает что его нет совсем и быть не может.)
Если доступа ко внешним данным никому не нужно, то я не рекомендую применять описаный шаблон. Даже запрещаю это делать.
Если же всё-таки он нужен, то компонент который инжектирует данные должен быть соответствующем образом отмечен, как и компонент, реализация которого допускает импорт данных из контейнера c явным указанием всех таких типов данных. Например, аттрибутами.
Есть ещё маркерные интерфейсы.
А сделать это очень просто…
BuildTop
. Применяя шаблон Data Dependency мы позволяем любой реализации ранжировщика получить доступ к этим данным, но не обязываем её это делать.В случае, когда никому из ранжировщиков эти данные не нужны — Data Dependency применять не нужно.
Шаблон полезно иметь в арсенале на случай, если такое требование появится позже. В этом случае мы просто применим рефакторинг «Convert method to Method Object» и шаблон Data Dependency. Изменения интерфейса
IRanker
не потребуется. Таким образом, мы удовлетворим новое требование и сохраним совместимость с имеющимися реализациями.«Композиция» реализовывала другой интерфейс, ибо результат подсчета композитного рейтинга не был целым числом. (В моем случае вообще не был числом).
Упомянутая возможность Unity мне хорошо известна и была использована в первую очередь. Проблема может возникнуть, если оценщики зависят друг от друга, именованые регистрации в Unity, к сожалению, друг о друге ничего не знают. Дефолтная реализация
IRankerProvider
делает именно то, что вы написали — разрешает массив. Но, сам интерфейс делается для того, чтобы поведеление можно было изменить. Можно ещё зарегистрировать InjectionFactory для массива. Как говорится, по желанию и никакого приведения типов.BuilerWorker
в даной ситуации больше похож на паттерн «MethodObject» и ничто не мешает его тестировать, при соответствующем изменении видимости.Но вы не решили проблему, с которой я начал — как в ранжировщики передавать «опциональные» параметры.
Это оправдано архитектурно, т.к. компоненты использующие DD могут использовать друг друга, а, следовательно, ранжировщики смогут получить доступ не только к данным, зарегистрированным в текушем контексте, но и к данным зарегистрированным во внешнем контексте по отношению к TopBuilder.