Комментарии 14
Правильно ли я понимаю, что при вызове
будет каким-то образом вызван конструктор с параметром?
Tweeter tweeter = new Tweeter();
будет каким-то образом вызван конструктор с параметром?
0
Не совсем. В случае с Dagger, мы убрали HttpClient из конструктора, поэтому у класса Tweeter остался только конструктор по умолчанию. Т.е. HttpClient указывается только в классе, которому он непосредственно необходим и нет необходимости «тянуть» его из родительских классов. Забыл добавить этот класс в вариант с Dagger — обновлю пост. Спасибо!
0
Вот это:
очень нехорошо. Убирая зависимость на классы бизнес-логики, вы добавляете зависимость на класс инжектора.
Самым правильным способом, имхо, является использование внедрения через конструктор. Guice умеет так (да и Dagger, я думаю, тоже):
и пусть теперь этот класс создаётся контейнером, когда потребуется его использование внутри проекта. Если же его нужно протестировать, в модульном тесте вы делаете просто
public TwitterApi()
{
//Добавляем класс в граф зависимостей
Injector.inject(this);
//На этом этапе "магическим" образом client проинициализирован Dagger'ом
}
очень нехорошо. Убирая зависимость на классы бизнес-логики, вы добавляете зависимость на класс инжектора.
TwitterApi
невозможно будет использовать отдельно от контейнера DI.Самым правильным способом, имхо, является использование внедрения через конструктор. Guice умеет так (да и Dagger, я думаю, тоже):
public class TwitterApi {
private final HttpClient client;
@Inject
public TwitterApi(HttpClient client) {
this.client = client;
}
...
}
и пусть теперь этот класс создаётся контейнером, когда потребуется его использование внутри проекта. Если же его нужно протестировать, в модульном тесте вы делаете просто
new TwitterApi(mockClient)
, без всяких контейнеров. Использование контейнеров в модульных тестах, кстати, есть плохой тон. +3
Не очень хорошо понимаю философию DI, поэтому, возможно, глупый вопрос. Как быть, если в некотором месте мне нужно насоздавать произвольное количество объектов, классом которых я хочу управлять с помощью DI-контейнера? Ну, в данном случае пусть мне надо создать массив
HttpClient
'ов. И как быть, если эти объекты сами имеют внедряемые зависимости? 0
Без проблем. Внедрите не сам объект, а его провайдер (в терминологии Guice, в Spring, кажется, это называется Factory):
и теперь можно использовать этот провайдер, чтобы насоздавать столько объектов, сколько нужно:
Все зависимости при этом будут разрешены автоматически. Тут ещё от scope'а привязки зависит. Если
Есть и другие паттерны создания и внедрения объектов. Например, Guice позволяет создавать объекты таким образом, чтобы часть зависимостей объекта внедрялась, а часть передавалась из кода бизнес-логики (расширение AssistedInject). Можно делать провайдеры, которые могут выбрасывать исключения, чтобы затем обрабатывать эти исключения в коде бизнес-логики (расширение ThrowingProviders). Можно сделать привязки на множество реализаций одного интерфейса и внедрить их все сразу как
public class TwitterApi {
private final Provider<HttpClient> clientProvider;
@Inject
public TwitterApi(Provider<HttpClient> clientProvider) {
this.clientProvider = clientProvider;
}
...
}
и теперь можно использовать этот провайдер, чтобы насоздавать столько объектов, сколько нужно:
List<HttpClient> httpClients = new ArrayList<>();
for (int i = 0; i < 10; ++i) {
httpClients.add(clientProvider.get());
}
Все зависимости при этом будут разрешены автоматически. Тут ещё от scope'а привязки зависит. Если
HttpClient
помечен как singleton, то контейнер сам позаботится, чтобы существовал только один экземпляр объекта, и провайдер всегда будет возвращать его. Если scope не указан, объекты будут создаваться на каждый вызов get()
.Есть и другие паттерны создания и внедрения объектов. Например, Guice позволяет создавать объекты таким образом, чтобы часть зависимостей объекта внедрялась, а часть передавалась из кода бизнес-логики (расширение AssistedInject). Можно делать провайдеры, которые могут выбрасывать исключения, чтобы затем обрабатывать эти исключения в коде бизнес-логики (расширение ThrowingProviders). Можно сделать привязки на множество реализаций одного интерфейса и внедрить их все сразу как
Set
или Map
, что бывает полезно для плагинной архитектуры (расширение Multibindings). Не знаю, как с этим у Dagger, но Guice умеет всё вышеперечисленное, и это очень удобно. +3
Спасибо.
0
Интересно, в спринге вроде бы по умолчанию как раз синглтон-скоуп…
Надо посмотреть наконец на этот Guice, а то все спринг да спринг :)
Надо посмотреть наконец на этот Guice, а то все спринг да спринг :)
0
Да, в спринге по умолчанию все бины — синглтоны. Ну это в некотором роде соответствует ментальной модели спринговой XML-конфигурации, когда вы описываете компоненты-бины. В Guice нет понятия бинов, там привязки реализаций к интерфейсам, и там создание новых копий объектов при внедрении более логично.
0
Ну попробую со своей колокольни ответить, не претендую на 100% правильность. В спринге (с даггером не работал, не знаю) можно задать bean scope = prototype. И при каждом инжекте этого бина вы будете получать новый инстанс. Насколько я понял, вам это нужно? Вообще есть много всяких скоупов.
+1
Все верно — в данном примере я показал Fields Injection. Есть еще Constructor Injection, о котором Вы говорите.
На самом деле, Injector.inject() рано или поздно прийдется сделать, потому что для кого-то TwitterApi будет полем, а при FieldInjection необходимо добавлять себя в контейнер.
Тем не менее, Ваш вариант явно лучше хотя бы тем, что TwitterApi модуль уже не зависит от контейнера. Опыта с DI у меня немного, поэтому на подобные грабли не наступал (пока :) )
На самом деле, Injector.inject() рано или поздно прийдется сделать, потому что для кого-то TwitterApi будет полем, а при FieldInjection необходимо добавлять себя в контейнер.
Тем не менее, Ваш вариант явно лучше хотя бы тем, что TwitterApi модуль уже не зависит от контейнера. Опыта с DI у меня немного, поэтому на подобные грабли не наступал (пока :) )
0
Injector.inject() рано или поздно прийдется сделать, потому что для кого-то TwitterApi будет полем, а при FieldInjection необходимо добавлять себя в контейнер.
Нет, не придётся. Нужно просто не делать
TwitterApi
внедряемым полем, и всё. Вы же контролируете ваш код, не так ли? :)Опыта с DI у меня немного, поэтому на подобные грабли не наступал (пока :) )
Всё ещё впереди) я начинал со спринга, и очень долго не мог понять, что такое DI вообще и зачем нужен спринг в частности. Потом щёлкнуло, и сразу всё стало понятно)
0
Зарегистрируйтесь на Хабре , чтобы оставить комментарий
Знакомимся с Dependency Injection на примере Dagger