[JAVA] Comprendre l'échantillon officiel Coffee of Dagger2

À propos de Dagger2, qui est une bibliothèque DI pour Java / Android, l'exemple officiel Coffee app Je voudrais expliquer le mécanisme basé sur (/ master / examples / simple / src / main / java / coffee). Cependant, l'application Coffee était un peu difficile à comprendre (@ Binds, Lazy, etc.), donc le code décrit ci-dessous a été légèrement modifié. Le Guide de l'utilisateur Dagger2 peut être compris avec des connaissances du haut vers le milieu (Singletons et liaisons étendues).

Qu'essayez-vous de faire

Ce que cet exemple essaie de faire est de créer une instance de la classe CoffeeMaker avec Dagger2.

La classe CoffeeMaker a les dépendances suivantes.

image.png https://docs.google.com/presentation/d/1fby5VeGU9CN8zjw4lAb2QPPsKRxx6mSwCe9q7ECNSJQ/pub?start=false&loop=false&delayms=3000&slide=id.p

Résoudre cette dépendance et obtenir une instance de CoffeeMaker peut être un peu délicat.

Heater heater = new ElectricHeater();
Pump pump = new Thermosiphon(heater);
CoffeeMaker coffeeMaker = new CoffeeMaker(heater, pump).maker();
coffeeMaker.brew();

En utilisant Dagger2, le code ci-dessus ressemble à ceci:

CoffeeMaker coffeeMaker = DaggerCoffeeShop.create().maker();
coffeeMaker.brew();

De cette manière, le code client CoffeeMaker est libéré de la complexité de la création de dépendances. Ce qui suit décrit comment Dagger2 crée ces dépendances.

Déclaration de dépendance

Dagger utilise un constructeur avec l'annotation @ Inject pour créer une instance.

class Thermosiphon implements Pump {
    private final Heater heater;

    @Inject
    Thermosiphon(Heater heater) {
        this.heater = heater;
    }

    @Override
    public void pump() {
        if (heater.isHot()) {
            System.out.println("=> => pumping => =>");
        }
    }
}

Si une instance Thermosiphon est demandée, une instance Thermosiphon sera créée à l'aide du constructeur ci-dessus.

Ici, la classe Thermosiphon dépend de la classe Heater. Dagger identifie les dépendances définies dans le constructeur et les résout.

Satisfaire les dépendances

Dagger essaie de satisfaire la dépendance en créant une instance du type demandé, mais la déclaration de dépendance par @ Inject ne fonctionne pas toujours. Dans les cas suivants, «@ Inject» ne fonctionnera pas.

--Type est une interface

Dans l'application Coffee, les types dont dépend CoffeeMaker (chauffage et pompe) sont déclarés comme interfaces, ce qui est le cas ci-dessus. Dans ces cas, la méthode annotée avec «@ Provides» crée une instance.

Par exemple, Heater déclare une dépendance comme suit: Définissez une méthode annotée avec «@ Provides» et spécifiez Heater comme type de retour.

@Provides
static Heater provideHeater() {
    return new ElectricHeater();
}

De même, Thermosiphon déclare: La méthode @ Provides peut avoir ses propres dépendances.

@Provides
static Pump providePump(Thermosiphon pump) {
    return pump;
}

La méthode @ Provides doit appartenir au module. Un module est une classe avec l'annotation @ Module.

@Module
class DripCoffeeModule {
    @Provides
    static Heater provideHeater() {
        return new ElectricHeater();
    }

    @Provides
    static Pump providePump(Thermosiphon pump) {
        return pump;
    }
}

Par convention, la méthode «@ Provides» semble avoir un préfixe provide et le module a un suffixe Module.

Construire un graphe de dépendances

Les classes et méthodes définies par @ Inject et @ Provides forment un graphe de dépendance d'un objet. Pour créer ce graphique, définissez un composant. Un composant est une interface annotée avec @ Component et a des méthodes sans arguments. Le type de retour de ces méthodes sera le type que vous souhaitez résoudre.

Ce qui suit définit un composant CoffeeShop avec une méthode qui renvoie une occurrence de CoffeeMaker. Passez des modules aux composants pour créer des dépendances subordonnées. Ici, nous passons le DripCoffeeModule.

@Component(modules = DripCoffeeModule.class)
interface CoffeeShop {
    CoffeeMaker maker();
}

Dagger générera automatiquement une implémentation de CoffeeShop basée sur cette définition. La classe d'implémentation générée par Dagger est préfixée par Dagger. Ici, une classe d'implémentation appelée DaggerCoffeeShop est générée.

La classe d'implémentation de composant a une méthode de générateur, qui est utilisée pour créer des dépendances.

CoffeeShop coffeeShop = DaggerCoffeeShop.builder()
    .dripCoffeeModule(new DripCoffeeModule())
    .build();

Dans l'exemple ci-dessus, une instance de DripCoffeeModule est créée manuellement et transmise au générateur, mais si les conditions suivantes sont remplies, ce processus est inutile.

--Si vous avez accès au constructeur par défaut du module --Si toutes les méthodes @ Provides du module sont statiques

Pour les composants, si tous les modules suivants s'appliquent à ce qui précède, la classe d'implémentation dispose d'une méthode de création qui vous permet de créer une instance directement sans passer par le générateur.

CoffeeShop coffeeShop = DaggerCoffeeShop.create();

Enfin, le code client CoffeeMaker ressemble à ceci:

CoffeeMaker coffeeMaker = DaggerCoffeeShop.create().maker();
coffeeMaker.brew();

Code de l'application Coffee (version modifiée)

Voici une version légèrement modifiée de l'application officielle du café (https://github.com/google/dagger/tree/master/examples/simple/src/main/java/coffee). Il est implémenté uniquement avec les fonctions de la plage expliquée ci-dessus.

** * Seule l'annotation @ Singleton sera décrite plus tard. ** **

Heater.java(Restez officiel)


interface Heater {
    void on();
    void off();
    boolean isHot();
}

Pump.java(Restez officiel)


interface Pump {
    void pump();
}

ElectricHeater(Restez officiel)


class ElectricHeater implements Heater {
    boolean heating;

    @Override
    public void on() {
        System.out.println("~ ~ ~ heating ~ ~ ~");
        this.heating = true;
    }

    @Override
    public void off() {
        this.heating = false;
    }

    @Override
    public boolean isHot() {
        return heating;
    }
}

Thermosiphon(Restez officiel)


class Thermosiphon implements Pump {
    private final Heater heater;

    @Inject
    Thermosiphon(Heater heater) {
        this.heater = heater;
    }

    @Override
    public void pump() {
        if (heater.isHot()) {
            System.out.println("=> => pumping => =>");
        }
    }
}

DripCoffeeModule.java


@Module
class DripCoffeeModule {
    @Singleton
    @Provides
    static Heater provideHeater() {
        return new ElectricHeater();
    }

    @Provides
    static Pump providePump(Thermosiphon pump) {
        return pump;
    }
}

CoffeeMaker(Restez officiel)


class CoffeeMaker {
    private final Heater heater;
    private final Pump pump;

    @Inject
    CoffeeMaker(Heater heater, Pump pump) {
        this.heater = heater;
        this.pump = pump;
    }

    public void brew() {
        heater.on();
        pump.pump();
        System.out.println(" [_]P coffee![_]P ");
        heater.off();
    }
}

CoffeeShop.java


@Singleton
@Component(modules = DripCoffeeModule.class)
public interface CoffeeShop {
    CoffeeMaker maker();
}

CoffeeApp.java


public class CoffeeApp {
     public static void main(String[] args) {
          CoffeeMaker coffeeMaker = DaggerCoffeeShop.create().maker();
          coffeeMaker.brew();
     }
}

Annotation @ Singleton

Lors de la résolution des dépendances, Dagger essaiera de créer une instance du type à chaque fois qu'elle est demandée par défaut. Cependant, dans le cas de l'application Coffee, le comportement par défaut ne fonctionne pas comme prévu.

La méthode d'infusion de CoffeeMaker est d'allumer le chauffage (chauffe.on ()), de verser de l'eau (pump.pump ()), de préparer du café (println ()) et de l'éteindre à nouveau. Ne versez de l'eau que lorsque le chauffage est allumé. Vérifier si le chauffage est allumé est délégué à l'instance Heat détenue par l'instance Pump, mais à ce stade, l'instance Heat détenue par l'instance CoffeeMaker et l'instance Heat détenue par l'instance Pump sont Vous devez faire référence à la même chose.

Comme mentionné ci-dessus, Dagger est créé chaque fois qu'un type est demandé, il ne se comportera donc pas comme prévu. Vous pouvez ensuite utiliser l'annotation @ Singleton pour spécifier que la même instance doit toujours être renvoyée lorsque le type est demandé.

Dans le code ci-dessus, la méthode provideHeater de DripCoffeeModule est annotée avec @ Singleton pour spécifier que l'instance Heater est singleton. L'interface CoffeeShop est également annotée avec @ Singleton afin que le composant et ses instances subordonnées aient le même cycle de vie.

Résumé

J'ai expliqué la partie de base qui est la première étape pour comprendre Dagger. Comme ce n'est que la première partie du guide de l'utilisateur, je pense qu'il y a encore beaucoup de connaissances qui ne sont pas suffisantes pour pratiquer Dagger, mais ceux qui veulent utiliser Dagger à partir de maintenant, et les miens quelques jours plus tard. J'ai pensé que cela vous serait utile.

Recommended Posts

Comprendre l'échantillon officiel Coffee of Dagger2
Lisez la documentation officielle de Dagger2 pour comprendre les bases
Comprendre le mécanisme de base de log4j2.xml
Comprendre les bases de l'enregistrement audio Android
Officiel de Zeller (demandant le jour)
À propos du guide de démarrage officiel de Spring Framework
Le nom officiel de Spring MVC est Spring Web MVC
[Pour les débutants] Comprendre rapidement les bases de Java 8 lambda
Comprendre les caractéristiques de Scala en 5 minutes (Introduction à Scala)
J'ai écrit un diagramme de séquence de l'exemple j.u.c.Flow
Le monde de Clara-Rules (2)
Jugement du calendrier
Le monde de Clara-Rules (4)
Le monde de Clara-Rules (1)
Comprenons la fonction!
Le monde de Clara-Rules (3)
Le monde de Clara-Rules (5)
L'idée du tri rapide
L'idée de jQuery
Suivre le mémorandum d'association de fonction (comprendre la description du modèle utilisateur)