Ниже вы увидите вольный перевод статьи Miquel Beltran, опубликованной на
Medium 12 февраля 2016 года. Целью статьи является формирование понимания базового механизма работы Dagger 2.
Я обнаружил, что большинство руководств, объясняющих как работает Dagger, слишком сложны. Даже руководство Google/Square слишком тяжелое для понимания, если у вас нет четкого представления о том, как работает внедрение зависимостей.
Чтобы понять это, я создал очень простой Java-проект с несколькими классами, который показывает, как работает Dagger.
В этой статье я объясню основные составляющие Dagger. Статья нацелена на тех, кто не пользуется данной библиотекой, но планирует.
Это код файла build.gradle. Будет использоваться стандартный java плагин и JUnit для создания модульных тестов. Не забудьте добавить в зависимости библиотеку dagger-compiler (это была моя первая ошибка).
В этом примере будет два класса:
Без внедрения зависимостей получилось бы что-то вроде следующего: класс GameData создается внутри GameSession. Некоторые разработчики согласятся с тем, что это плохая практика. Например, если требуется другой экземпляр GameData для тестирования, то создать его будет невозможно.
Dagger позаботится за нас о внедрении экземпляра класса GameData в переменную класса GameSession. Необходимо лишь указать это с помощью аннотации
Теперь нужно создать классы, которые определят, как будет реализовано внедрение зависимостей, это Component и Module.
В этом моменте заключена вся магия. Dagger поймет, что классу GameSession требуется класс GameData, что класс GameModule определяет, как будет получен экземпляр GameData; что необходимо воспользоваться интерфейсом GameComponent для начала инъекции, вызываемой из класса GameSession.
Следующий модульный тест показывает, как внедрить класс GameData в созданный экземпляр класса GameSession, используя сгенерированный класс типа Component.
Этот пример прост, в нём пропущено множество функциональных возможностей Dagger. Как я уже сказал, этот пример помог мне понять основы, и я надеюсь, что он поможет и вам.
Теперь, когда у вас есть общее представление о том, как работает Dagger 2, я рекомендую вернуться к исходной документации и попробовать разобрать пример с CoffeeMaker.
Medium 12 февраля 2016 года. Целью статьи является формирование понимания базового механизма работы Dagger 2.
Я обнаружил, что большинство руководств, объясняющих как работает Dagger, слишком сложны. Даже руководство Google/Square слишком тяжелое для понимания, если у вас нет четкого представления о том, как работает внедрение зависимостей.
Чтобы понять это, я создал очень простой Java-проект с несколькими классами, который показывает, как работает Dagger.
В этой статье я объясню основные составляющие Dagger. Статья нацелена на тех, кто не пользуется данной библиотекой, но планирует.
Структура проекта
apply plugin: 'java'
repositories {
jcenter()
}
dependencies {
testCompile 'junit:junit:4.12'
compile 'com.google.dagger:dagger:2.0'
compile 'com.google.dagger:dagger-compiler:2.0'
}
Это код файла build.gradle. Будет использоваться стандартный java плагин и JUnit для создания модульных тестов. Не забудьте добавить в зависимости библиотеку dagger-compiler (это была моя первая ошибка).
Покажите мне код
В этом примере будет два класса:
- GameData: этот класс предоставляет данные, необходимые классу GameSession. В нашем случае — просто строка.
- GameSession: этому классу требуется GameData. Мы будем внедрять зависимость с помощью Dagger, вместо передачи GameData параметром или создания экземпляра внутри GameSession.
Без внедрения зависимостей получилось бы что-то вроде следующего: класс GameData создается внутри GameSession. Некоторые разработчики согласятся с тем, что это плохая практика. Например, если требуется другой экземпляр GameData для тестирования, то создать его будет невозможно.
public class GameData {
public final String hello = "Hello Dagger";
}
public class GameSession {
public GameData data = new GameData();
}
Dagger позаботится за нас о внедрении экземпляра класса GameData в переменную класса GameSession. Необходимо лишь указать это с помощью аннотации
@Inject
.public class GameData {
public final String hello = "Hello Dagger";
}
import javax.inject.Inject;
public class GameSession {
@Inject
public GameData data;
}
Теперь нужно создать классы, которые определят, как будет реализовано внедрение зависимостей, это Component и Module.
- Module определяет всех поставщиков инъекций. То есть определяет методы, которые вернут нам конкретные экземпляры для внедрения в зависимый класс. В данном случае нужно определить поставщика, который вернет нам GameData.
- Component — это интерфейс, который Dagger будет использовать для генерации кода, чтобы внедрить зависимости за нас.
import dagger.Component;
@Component(modules = GameModule.class)
public interface GameComponent {
void inject(GameSession obj);
}
import dagger.Module;
import dagger.Provides;
@Module
public class GameModule {
@Provides
GameData providesGameData() {
return new GameData();
}
}
- GameModule содержит функцию с аннотацией
@Provides
, которая говорит Dagger, что это одна из тех функций, которая возвращает экземпляр GameData. - Необходимо также отметить класс GameModule аннотацией
@Module
. - Интерфейс GameComponent определяет функцию инъекции. Эта функция будет использоваться в классе GameSession и внедрит в него экземпляр класса GameData, который вернет функция GameModule.providesGameData().
В этом моменте заключена вся магия. Dagger поймет, что классу GameSession требуется класс GameData, что класс GameModule определяет, как будет получен экземпляр GameData; что необходимо воспользоваться интерфейсом GameComponent для начала инъекции, вызываемой из класса GameSession.
Использование Component
Следующий модульный тест показывает, как внедрить класс GameData в созданный экземпляр класса GameSession, используя сгенерированный класс типа Component.
import org.junit.Test;
import static org.junit.Assert.*;
public class GameSessionTest {
@Test
public void testGameSession() {
GameSession session = new GameSession();
DaggerGameComponent.create().inject(session);
assertEquals("Hello Dagger", session.data.hello);
}
}
- Класс DaggerGameComponent сгенерирован Dagger, включая функцию create().
- Этот класс реализует интерфейс GameComponent, в котором мы можем вызвать метод inject() и передать в него экземпляр GameSession.
- Метод inject() позаботится о том, чтобы внедрить все зависимости для GameSession.
- Наконец, мы можем увидеть, что значение поля data класса GameSession установлено.
Этот пример прост, в нём пропущено множество функциональных возможностей Dagger. Как я уже сказал, этот пример помог мне понять основы, и я надеюсь, что он поможет и вам.
Теперь, когда у вас есть общее представление о том, как работает Dagger 2, я рекомендую вернуться к исходной документации и попробовать разобрать пример с CoffeeMaker.
ScratchBoom
сколько раз не пытался понять, чем даггер удобнее dependency injection через конструктор — так и не понял.
tehreh1uneh Автор
Я не такой хороший специалист в этой области, чтобы полноценно и внятно ответить на вопрос. Сейчас читаю ещё одну свежую серию статей на medium. Как найду время — опубликую перевод здесь. Автор статей разбирает основы, начиная с базовых идей DI. В этой серии статей можно будет найти ответ на вопрос, чем же хороша идея DI в сравнении с использованием связи через конструктор.
akardapolov
DI через конструктор можно делать с или без Dagger.
Dagger 2 и структура приложения для Android
Дружественное введение в Dagger 2. Часть 1
andrikeev
Инъекция через конструктор как раз является приоритетным способом при использовании Dagger. Но иногда это невозможно, например, когда созданием экземпляра объекта занимается фреймворк, и конструктор не должен содержать параметров. Например Activity/Fragment/Service в Android.