Внедрение зависимостей с Dagger 2. Часть 2.

В этой статье, которая во второй раз посвящена фреймворку для DI, а именно Dagger 2, я решил более подробно рассказать о дополнительных возможностях фреймворка. В первой статье мы познакомились с основными концепциями Dagger 2 и смогли использовать его возможности при создании практического примера. Идем дальше.

Аннотация @Singleton.

Если вы не знакомы с паттерном Singleton, то вам сюда. Часто нам нужно в проекте, чтобы объект какого-то класса существовал в системе лишь в одном экземпляре. Возможно, это соединение с базой данных, http-клиент или класс настроек вроде SharedPreferences. Так или иначе, если вам это необходимо, то вы можете сообщить о своих намерениях Dagger при помощи аннотации @Singleton:

Одна аннотация — и Dagger позаботится о том, чтобы нужный вам объект оставался в вашей системе в единственном числе.

На самом деле аннотация @Singleton относится к более общей теме — областям существования (scopes).

Scopes.

Возможно вы задумывались над тем: а как долго должны жить созданные компоненты? В Dagger компоненты существуют ровно столько, сколько существует то пространство, тот scope, в котором они объявлены. Если бы компоненты были, предположим, помечены некоторой аннотацией @ApplicationScoped, то они бы существовали на протяжении всей жизни приложения. Если бы они имели в своем объявлении аннотацию @ActivityScoped, то существовали до тех пор, пока существует активность, в которой эти компоненты объявлены.

На самом деле указанных выше аннотаций в Dagger нет, но их мы можем создать самостоятельно. Единственная аннотация, описывающая область жизни компонентов, доступная, что говорится, «из коробки» — это приведенный выше @Singleton.

Как определить свою аннотацию? Это сделать очень просто. Допустим, что у нас есть окно, в котором мы формируем вывод документов на печать. Объекты, которые могут понадобиться нам в этом custom scope, можно пометить, определенной ниже аннотацией:

Теперь те объекты, которые нужны нам только в определенном scope, мы помечаем этой аннотацией. Например, у нас есть объект Printer, выполняющий непосредственно печать:

А вот так к слову определяется описанная выше аннотация @Singleton:

Как видите, отличий не слишком много.

Зависимости компонентов (Component dependencies).

Вероятно, теперь по аналогии со scope вы захотите создать отдельные компоненты для разных задач. Более общий AppComponent будет предоставлять глобальные объекты, которые нужны всем. Другие же компоненты будут определять только локальные зависимости для конкретных нужд, например инжектировать для нашего модуля печати только те объекты, которые нужны ему. Итого у нас будет:

1.) AppComponent — предоставляющий базовые объекты, вроде контекста, настроек или соединения с базой данных.

2). PrintComponent — компонент, доставляющий объекты модулю печати.

PrintComponent также зависит от AppComponent, так как ему могут понадобиться те объекты, которые он предоставляет. Поэтому для того, чтобы Dagger знал об этих зависимостях, в определении компонента это нужно явно указать, добавив в аннотации @Component параметр dependencies:

Subcomponents.

Subcomponent — во многом напоминает то, о чем мы говорили чуть выше. Subcomponent — это все то же управления зависимостями компонентов. В чем отличия?

Subcomponent может иметь только один родительский компонент. При этом он получает доступ ко всем его объектам. Допустим, у нас есть некоторый объект в программе, занимающийся сбором статистики. Этому объекту требуется ряд зависимостей, которые ему может предоставить созданный для него StatComponent.  Этому компоненту в свою очередь нужны некоторые зависимости, предоставляемые AppComponent. В принципе, раз он от него зависит, то мы бы могли воспользоваться примером выше, указав для него нужную зависимость:

Но мы пойдем другим путем. Для начала определим наш  StatComponent как Subcomponent. Это можно сделать при помощи аннотации @Subcomponent.

В этом случае у родительского компонента надо явно прописать метод для получения его дочернего компонента:

Далее предположим, что у нашего StatComponent есть еще один дочерний компонент — PrintComponent. Он предоставляет нужные зависимости для модуля печати, но ему необходим доступ как к некоторым объектам по сбору статистики, так и к глобальным объектам, определенным в AppComponent.

Не вопрос. Точно также определим наш  PrintComponent как Subcomponent.

Также изменяем его родительский компонент:

Так зачем же мы все это делали?

Суть в том, что самому последнему компоненту во всей иерархии — PrintComponent, будут доступны все зависимости по всему этому дереву компонент. То есть, если в AppComponent определен UtilModule, то PrintComponent сможет получить нужные ему объекты, доступные из этого модуля.

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *