Как стать автором
Обновить

Понимание Dagger 2

Время на прочтение3 мин
Количество просмотров22K
Ниже вы увидите вольный перевод статьи Miquel Beltran, опубликованной на
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);
    }
}

  1. Класс DaggerGameComponent сгенерирован Dagger, включая функцию create().
  2. Этот класс реализует интерфейс GameComponent, в котором мы можем вызвать метод inject() и передать в него экземпляр GameSession.
  3. Метод inject() позаботится о том, чтобы внедрить все зависимости для GameSession.
  4. Наконец, мы можем увидеть, что значение поля data класса GameSession установлено.

Этот пример прост, в нём пропущено множество функциональных возможностей Dagger. Как я уже сказал, этот пример помог мне понять основы, и я надеюсь, что он поможет и вам.
Теперь, когда у вас есть общее представление о том, как работает Dagger 2, я рекомендую вернуться к исходной документации и попробовать разобрать пример с CoffeeMaker.
Теги:
Хабы:
Всего голосов 10: ↑10 и ↓0+10
Комментарии6

Публикации

Истории

Работа

Java разработчик
350 вакансий

Ближайшие события