Ниже вы увидите вольный перевод статьи Miquel Beltran, опубликованной на
Medium 12 февраля 2016 года. Целью статьи является формирование понимания базового механизма работы Dagger 2.
Я обнаружил, что большинство руководств, объясняющих как работает Dagger, слишком сложны. Даже руководство Google/Square слишком тяжелое для понимания, если у вас нет четкого представления о том, как работает внедрение зависимостей.
Чтобы понять это, я создал очень простой Java-проект с несколькими классами, который показывает, как работает Dagger.
В этой статье я объясню основные составляющие Dagger. Статья нацелена на тех, кто не пользуется данной библиотекой, но планирует.
Это код файла build.gradle. Будет использоваться стандартный java плагин и JUnit для создания модульных тестов. Не забудьте добавить в зависимости библиотеку dagger-compiler (это была моя первая ошибка).
Замечание от foal по поводу зависимости 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 (это была моя первая ошибка).
Замечание от foal по поводу зависимости dagger-compiler:
Для Dagger‡ и других APT-based продуктов лучше пользоваться gradle-apt-plugin. Это сделает компиляцию вашего проекта более прозрачной и безопасной, так как не будут путаться два разных classpath'а — один для компиляции вашего кода и второй для его генерации.
Покажите мне код
В этом примере будет два класса:
- 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.
