Данная статья является пятой частью серии статей, предназначенных, по словам автора, для тех, кто не может разобраться с внедрением зависимостей и фреймворком Dagger 2, либо только собирается это сделать. Оригинал написан 17 декабря 2017 года. Перевод вольный.

Это пятая статья цикла «Dagger 2 для начинающих Android разработчиков.». Если вы не читали предыдущие, то вам сюда.
В предыдущей статье мы обсуждали как ручное использование внедрения зависимостей (DI) усложняет работу и увеличивает количество шаблонного кода. После рассмотрели как Dagger 2 избавляет нас от этой боли и генерирует шаблонный код за нас. Также пробежались по обработчикам аннотаций и базовым аннотациям Dagger 2. Затем применили эти аннотации на примере и внедрили зависимости с помощью Dagger 2.
Для лучшего понимания Dagger 2 рассмотрим класс
Вот что генерирует Dagger 2 за нас для решения проблемы сильных связей (hard dependency). Если посмотреть на интерфейс, который реализует класс, то вы увидите, что это
Эта зависимость предоставляется с использованием шаблона
Я надеюсь, что вы четко понимаете, зачем нужен метод
После внесения изменений заново соберите проект. Теперь проверим класс
Как видно, Dagger 2 реализовал методы
Мы указали Dagger 2 получить эти зависимости с помощью аннотации
Ничего не произошло? Да, вы не получили никакой ошибки, но попробуйте запустить проект. Вы должны получить следующую ошибку:

Если прочитаете текст ошибки, то он явно говорит о том, что методы
Как ранее упоминалось, Dagger 2 позволяет легко отслеживать ошибки. Можете немного поиграть с этим классом.
Аннотации
Копнем глубже и разберемся с парой полезных аннотаций —
Если кратко, то эта аннотация отмечает модули и классы. Поговорим об Android. У нас может быть модуль
Если кратко, то данная аннотация нужна для пометки методов, которые предоставляют зависимости, внутри модулей. В ранее описанном примере мы пометили класс
Посмотрите небольшой пример (ссылка на ветку).
Возьмем два сервиса, предоставляемых Браавосом — деньги (Cash) и солдат (Soldiers) (Я не уверен, что они предоставляют такие услуги, но рассмотрим это только для примера). Создадим два класса:
Теперь создадим модуль и назовем его
Как мы видели ранее, необходимо пометить все модули аннотацией
Вернемся к классу
Обратите внимание на то, что модуль добавлен в объявление аннотации
После всех изменений соберите проект заново. Вы увидите ошибку в методе
После включения всех модулей вы можете начать использовать их методы через
Если вы хотите убедиться, то наведите курсор на
Мы проанализировали генерируемые Dagger 2 классы и заметили, что Dagger 2 использует шаблон
В следующей статье мы рассмотрим пример Android приложения с использованием Dagger 2.
Следующая статья выйдет 29 декабря или позже. Спасибо за терпение.

Это пятая статья цикла «Dagger 2 для начинающих Android разработчиков.». Если вы не читали предыдущие, то вам сюда.
Серия статей
- Dagger 2 для начинающих Android разработчиков. Введение.
- Dagger 2 для начинающих Android разработчиков. Внедрение зависимостей. Часть 1 .
- Dagger 2 для начинающих Android разработчиков. Внедрение зависимостей. Часть 2.
- Dagger 2 для начинающих Android разработчиков. Dagger 2. Часть 1.
- Dagger 2 для начинающих Android разработчиков. Dagger 2. Часть 2 (вы здесь).
- Dagger 2 для начинающих Android разработчиков. Dagger 2. Продвинутый уровень.
Часть 1. - Dagger 2 для начинающих Android разработчиков. Dagger 2. Продвинутый уровень.
Часть 2.
Ранее в цикле статей
В предыдущей статье мы обсуждали как ручное использование внедрения зависимостей (DI) усложняет работу и увеличивает количество шаблонного кода. После рассмотрели как Dagger 2 избавляет нас от этой боли и генерирует шаблонный код за нас. Также пробежались по обработчикам аннотаций и базовым аннотациям Dagger 2. Затем применили эти аннотации на примере и внедрили зависимости с помощью Dagger 2.
Анатомия DaggerBattleComponent
Для лучшего понимания Dagger 2 рассмотрим класс
DaggerBattleComponent. Установите курсор на DaggerBattleComponent и нажмите Ctrl+B (или Ctrl+ЛКМ, Command+ЛКМ). Вы увидите следующее:@Generated( value = "dagger.internal.codegen.ComponentProcessor", comments = "https://google.github.io/dagger" ) public final class DaggerBattleComponent implements BattleComponent { private DaggerBattleComponent(Builder builder) {} public static Builder builder() { return new Builder(); } public static BattleComponent create() { return new Builder().build(); } // реализованный метод интерфейса @Override public War getWar() { return new War(new Starks(), new Boltons()); } public static final class Builder { private Builder() {} public BattleComponent build() { return new DaggerBattleComponent(this); } } }
Вот что генерирует Dagger 2 за нас для решения проблемы сильных связей (hard dependency). Если посмотреть на интерфейс, который реализует класс, то вы увидите, что это
BattleComponent — интерфейс, который мы ранее создали и описали в нем метод getWar() для предоставления экземпляра класса War.Эта зависимость предоставляется с использованием шаблона
builder. О данном шаблоне подробнее модно прочитать здесь и здесь.Изучим кое-что новое
Я надеюсь, что вы четко понимаете, зачем нужен метод
getWar(). Сейчас я хочу добавить ещё пару зависимостей: Starks и Boltons. Добавим методы в интерфейс:@Component interface BattleComponent { War getWar(); // добавляем методы Starks getStarks(); Boltons getBoltons(); }
После внесения изменений заново соберите проект. Теперь проверим класс
DaggerBattleComponent. Если вы всё сделали верно, то увидите следующее.@Generated( value = "dagger.internal.codegen.ComponentProcessor", comments = "https://google.github.io/dagger" ) public final class DaggerBattleComponent implements BattleComponent { private DaggerBattleComponent(Builder builder) {} public static Builder builder() { return new Builder(); } public static BattleComponent create() { return new Builder().build(); } @Override public War getWar() { return new War(getStarks(), getBoltons()); } @Override public Starks getStarks() { return new Starks(); } @Override public Boltons getBoltons() { return new Boltons(); } public static final class Builder { private Builder() {} public BattleComponent build() { return new DaggerBattleComponent(this); } } }
Как видно, Dagger 2 реализовал методы
getStarks() и getBoltons().Мы указали Dagger 2 получить эти зависимости с помощью аннотации
@Inject в классе Boltons. Давайте кое-что сломаем. Уберите аннотацию @Inject из класса Boltons. Соберите проект заново.Ничего не произошло? Да, вы не получили никакой ошибки, но попробуйте запустить проект. Вы должны получить следующую ошибку:

Если прочитаете текст ошибки, то он явно говорит о том, что методы
getWar() и getBoltons() не будут работать, если нет пометок аннотациями @Inject или @Provides.Как ранее упоминалось, Dagger 2 позволяет легко отслеживать ошибки. Можете немного поиграть с этим классом.
Аннотации @Module и @Provides
Копнем глубже и разберемся с парой полезных аннотаций —
@Module и @Provides. Их стоит использовать, если размер вашего проекта увеличивается.@Module
Если кратко, то эта аннотация отмечает модули и классы. Поговорим об Android. У нас может быть модуль
ContextModule и этот модуль будет предоставлять зависимости ApplicationContext и Context для других классов. Для этого мы должны пометить класс ContextModule аннотацией @Module.@Provides
Если кратко, то данная аннотация нужна для пометки методов, которые предоставляют зависимости, внутри модулей. В ранее описанном примере мы пометили класс
ContextModule аннотацией @Module, но также нам необходимо пометить методы, которые предоставляют зависимости ApplicationContext и Context аннотацией @Provides.Посмотрите небольшой пример (ссылка на ветку).
Пример
Возьмем два сервиса, предоставляемых Браавосом — деньги (Cash) и солдат (Soldiers) (Я не уверен, что они предоставляют такие услуги, но рассмотрим это только для примера). Создадим два класса:
public class Cash { public Cash(){ // что-то происходит } }
public class Soldiers { public Soldiers(){ // что-то происходит } }
Теперь создадим модуль и назовем его
BraavosModule. Он будет снабжать нас двумя зависимостями — Cash и Soldiers.@Module // Модуль public class BraavosModule { Cash cash; Soldiers soldiers; public BraavosModule(Cash cash, Soldiers soldiers){ this.cash=cash; this.soldiers=soldiers; } @Provides // Предоставляет зависимость Cash Cash provideCash(){ return cash; } @Provides // Предоставляет зависимость Soldiers Soldiers provideSoldiers(){ return soldiers; } }
Как мы видели ранее, необходимо пометить все модули аннотацией
@Module, а методы, предоставляющие зависимости — аннотацией @Provides.Вернемся к классу
BattleOfBastards и укажем компоненту реализовывать методы provideCash() и provideSoldiers().@Component(modules = BraavosModule.class) interface BattleComponent { War getWar(); Cash getCash(); Soldiers getSoldiers(); }
public class BattleOfBastards { public static void main(String[] args){ Cash cash = new Cash(); Soldiers soldiers = new Soldiers(); BattleComponent component = DaggerBattleComponent .builder() .braavosModule(new BraavosModule(cash, soldiers)) .build(); War war = component.getWar(); war.prepare(); war.report(); // используем деньги и солдат component.getCash(); component.getSoldiers(); } }
Обратите внимание на то, что модуль добавлен в объявление аннотации
@Component. Это говорит о том, что компонент будет содержать внутри себя данный модуль.@Component(modules = BraavosModule.class)После всех изменений соберите проект заново. Вы увидите ошибку в методе
.create() класса DaggerBattleComponent. Она возникла в связи с тем, что при добавлении модуля необходимо передать эту зависимость Dagger 2. Выглядит это так:BattleComponent component = DaggerBattleComponent.builder().braavosModule(new BraavosModule(cash, soldiers)).build();После включения всех модулей вы можете начать использовать их методы через
Component.component.getCash(); component.getSoldiers();Если вы хотите убедиться, то наведите курсор на
DaggerBattleComponent и нажмите Ctrl+B (или Ctrl+ЛКМ, Command+ЛКМ). Вы увидите, что модуль BraavosModule включен в класс для предоставления зависимостей Cash и Soldiers.Резюме
Мы проанализировали генерируемые Dagger 2 классы и заметили, что Dagger 2 использует шаблон
builder для предоставления зависимостей. Также рассмотрели простой пример использования аннотаций @Module и @Provides.Что дальше?
В следующей статье мы рассмотрим пример Android приложения с использованием Dagger 2.
Следующая статья выйдет 29 декабря или позже. Спасибо за терпение.
