web-dev-qa-db-fra.com

Le problème de dague 2 écrasant en simple méthode annotée à partir d’un module de bibliothèque à utiliser en utilisation

Lien vers le projet GitHub

J'ai fait un projet sur GitHub qui est un modèle de l'architecture de poignard 2 de l'architecture réelle de mes projets. Cette question sera basée sur le projet GitHub.

J'ai fourni de nombreux extraits de code dans cette question. Toutefois, il peut être plus simple de compiler vous-même le projet dans Android Studio pour comprendre le problème.

Si vous extrayez le code, il ne compilera pas. Allez dans AppModule.Java et commentez les deux fournit des méthodes et il devrait compiler.

La question principale est la dernière ligne de ce post.

https://github.com/qazimusab/Dagger2LibraryProject

Architecture

J'ai une bibliothèque qui contient tout le code nécessaire pour faire l'application. Le point essentiel de cette architecture est que chaque application que je crée dans le projet devrait pouvoir utiliser la bibliothèque et, via le poignard 2, être en mesure de fournir différentes implémentations pour chaque classe ou activité de son choix dans son propre module. À ce stade, je n'ai qu'une seule application dans cet exemple de projet qui utilise la bibliothèque.

Le problème

Avec Dague 1, j'avais la même architecture, et dans le module spécifique à l'application (par opposition au module de bibliothèque), j'ai pu ajouter une nouvelle méthode fournit annotée pour remplacer toute implémentation fournie dans l'un des modules de bibliothèque. aussi longtemps que

  1. La méthode était dans un module dans le module d'application
  2. La méthode a été annotée avec @Provides
  3. La méthode avait le même type de retour que celui que vous voulez remplacer

Avec Dagger 2, l’architecture fonctionne lorsque je ne substitue aucune fourniture ou, le cas échéant, lorsque je substitue chaque fourniture dans ce module et que je supprime ce module de l’inclus du module spécifique à l’Application.

Par exemple, dans mon projet, j'ai une application et une bibliothèque.

L'application dispose d'un AppModule; la bibliothèque a un module CatModule pour fournir un chat et une nourriture CatFood, un module chien pour un chien et une nourriture DogFood, et un module bibliothèque pour fournir les activités.

CatModule.Java

package com.example.qaziahmed.library.application.modules;

import com.example.qaziahmed.library.classes.Cat; import
com.example.qaziahmed.library.classes.CatFood; import
com.example.qaziahmed.library.classes.contract.ICat; import
com.example.qaziahmed.library.classes.contract.ICatFood;

import javax.inject.Singleton;

import dagger.Module; import dagger.Provides;

/**  * Created by qaziahmed on 11/23/15.  */ @Module public class
CatModule {

    @Provides
    @Singleton
    ICat provideCat() {
        return new Cat();
    }

    @Provides
    ICatFood provideCatFood(){
        return new CatFood();
    } }

DogModule.Java

package com.example.qaziahmed.library.application.modules;

import com.example.qaziahmed.library.classes.Dog; import
com.example.qaziahmed.library.classes.DogFood; import
com.example.qaziahmed.library.classes.contract.IDog; import
com.example.qaziahmed.library.classes.contract.IDogFood;

import javax.inject.Singleton;

import dagger.Module; import dagger.Provides;

/**  * Created by qaziahmed on 11/23/15.  */ @Module public class
DogModule {

    @Provides
    @Singleton
    IDog provideDog() {
        return new Dog();
    }

    @Provides
    IDogFood provideDogFood(){
        return new DogFood();
    }

}

Donc, dans mon module d’application, je souhaite fournir une implémentation de chat domestique à la place de chat générique et une implémentation de IDogFood pour AllNaturalDogFood au lieu d’un simple DogFood, puis dans mon AppModule, j’ajoute deux

AppModule.Java

package com.example.qaziahmed.dagger2libraryproject.application;

import
com.example.qaziahmed.dagger2libraryproject.classes.AllNaturalDogFood;
import com.example.qaziahmed.dagger2libraryproject.classes.HouseCat;
import com.example.qaziahmed.library.application.modules.CatModule;
import com.example.qaziahmed.library.application.modules.DogModule;
import
com.example.qaziahmed.library.application.modules.LibraryModule;
import com.example.qaziahmed.library.classes.contract.ICat; import
com.example.qaziahmed.library.classes.contract.IDogFood;

import javax.inject.Singleton;

import dagger.Module; import dagger.Provides;

/**  * Created by ogre on 2015-07-12  */ @Module(includes = {
        LibraryModule.class,
        DogModule.class,
        CatModule.class }) public class AppModule {

    @Provides
    @Singleton
    ICat provideHouseCat() {
        return new HouseCat();
    }

    @Provides
    IDogFood provideAllNaturalDogFood(){
        return new AllNaturalDogFood();
    } }

Maintenant, quand j'exécute cette configuration, c'est l'erreur que je reçois:

Erreur: com.example.qaziahmed.library.classes.contract.ICat est lié plusieurs fois: @Provides @Singleton com.example.qaziahmed.library.classes.contract.ICat.example.qaziahmed.dagger2libraryproject.application.AppModule.provideHouseCat () @Provides @Singleton com.example.qaziahmed.library.classes.contract.Contract.ICat com.example.qaziahmed.library.application.modules.CatModule.provideCat () Erreur: com.example.qaziahmed.library.classes.classes.contract. IDogFood est lié à plusieurs reprises: @Provides com.example.qaziahmed.library.classes.contract.Contrat.IDogFood com.example.qaziahmed.dagger2libraryproject.application.AppModule.provideAllNaturalDogFood () @Provides com.example.lash.histoire. IDogFood com.example.qaziahmed.library.application.modules.DogModule.provideDogFood ()

Maintenant, si dans AppModule.Java, j'ajoute également des méthodes annotées pour fournir de la nourriture pour chats et du chien, puis supprimer CatModule.class et DogModule.class du module includes dans App Module, puis cela fonctionne.

Cependant, toute la question est de savoir comment remplacer une méthode unique dans certains modules de la bibliothèque sans avoir à remplacer chaque méthode annotée dans ce module spécifique, puis en supprimant ce module de la inclut dans AppModule.Java

12
qazimusab

Je vais essayer de décrypter cette citation de la dague Dagger 2:

Le poignard 2 ne prend pas en charge les substitutions. Les modules qui remplacent des simulations de test simples peuvent créer une sous-classe du module pour émuler ce comportement. Les modules qui utilisent des substitutions et dépendent de l'injection de dépendance doivent être décomposés de manière à ce que les modules remplacés soient représentés par un choix entre deux modules.

Dans votre exemple actuel, vous ne vous fiez pas à l’injection de dépendance, car vos méthodes provides* créent de nouveaux objets simples. Vous ne pourrez donc créer qu’une sous-classe du module, remplacer la méthode provides dont vous avez besoin et remplacer ce module par la suite. composant.

Lorsque vous comptez sur DI (et en réalité, à un moment donné de votre projet), procédez comme suit:

@Provides
@Singleton
ICat provideCat(IBowtie bowtie) { // 'bowtie' needs to be injected
    return new CatWithBowtie(Bowtie);
}

il s'agit de "Les modules qui utilisent des substitutions et reposent sur l'injection de dépendance devraient être décomposés", ce qui signifie: vous devez diviser CatModule en deux: CatModule avec juste providesCat et 'CatFoodModule' avec provideCatFood(). Ensuite, le composant de votre application vous utilisez simplement votre nouvelle CatWithBowtieModule au lieu de CatModule.

Il y a deux conseils utiles:

  1. Dans les projets de bibliothèque, les modules sont divisés en modules, il n'y a donc qu'une méthode provides* par module. Oui, cela ressemble à BS, mais c’est le seul moyen d’offrir une substitution facile plus tard dans votre application.

  2. Supposons un instant que la bibliothèque vous a été attribuée par un tiers en tant que JAR/AAP et que vous n’en ayez même pas la source. Dans ce cas, vous ne pourrez pas réutiliser les modules définis dans la bibliothèque, vous devrez donc tous les créer vous-même. C'est exactement ce qui se passe avec Dagger 2.

Lorsque vous essayez d'utiliser des modules de votre bibliothèque directement dans votre application (comme vous l'avez fait auparavant), ces deux projets ne sont plus deux projets distincts, mais un seul projet qui ressemble à deux projets (qui sont étroitement couplés à clusterf * ck). Il est normal que l’application dépende de la bibliothèque, mais c’est pas OK pour que la bibliothèque dépende de l’application. Cela se résume à: Dans Dagger 2, il est préférable de ne pas utiliser les bordures (projets) modules et components.

Quelqu'un peut demander: "Quel est l'intérêt d'utiliser Dagger 2 dans une bibliothèque si je ne peux pas utiliser la variable modules/components de lib dans mon application ?!". Eh bien, vous pourrez toujours utiliser votre dague modules/components dans vos tests unitaires, ce qui constitue le principal avantage de l'utilisation de Dagger après tout. De plus, si votre bibliothèque est destinée à être utilisée par d'autres personnes, vous pouvez (devez?) Fournir une application de référence qui montre comment "câbler" les choses afin que les utilisateurs de la bibliothèque puissent simplement copier ce code si cela leur convient ou au moins voir comment commencer.

8
Ognyan

Le problème est que votre injection voit deux méthodes qui fournissent le même objet.

Si vous lisez ce lien: http://google.github.io/dagger/ , vous pouvez y remédier en nommant votre fournisseur:

@Provides @Named("water")

Ensuite, dans votre injection, référencez-le comme suit:

@Inject @Named("water")
8
JFPicard

En guise de solution de contournement, j'écris deux méthodes, une pour @Provides et une pour @Overrides.

@Override
protected X getX() {
    return new X();
}

@Provides
X provideX() {
    return getX();
}
0
theapache64