66 votes

Dague 2: Injection du paramètre saisi par l'utilisateur dans un objet

Dire que j'ai une classe Util qui prend un objet, une instance de la classe Validator.

Car je veux éviter de l'instanciation de la classe de validation au sein Util, je passe via un constructeur:

public class Utils {

   @Inject
   public Util(Validator validator) {

   }


}

J'ai un module qui fournit le programme de validation d'instance:

@Provides
@Singleton
PhoneNumberUtil provideValidator() {
    return Validator.getInstance();
}

et une instance de la classe Util:

@Provides
Util provideUtil(Validator validator) {
    return new Utils(validator);
}

J'ai un composant connecté qui pourrait me donner un exemple d'Util:

Util getUtil()

dans mon activité, je pourrais l'appeler comme:

Util myUtil = getComponent.getUtil();

Tout cela fonctionne très bien - myUtil a un bon exemple de classe de validation lorsqu'il est instancié.

Maintenant je veux passer à une variable Chaîne nommée adresse (qui est la saisie de l'utilisateur via une interface utilisateur (IU). Je veux changer de constructeur, donc je passe à la fois une instance de programme de validation et de l'utilisateur saisie de Chaîne de caractères:

@Inject
public Util(Validator validator, String address) {

}

J'ai juste ne peux pas obtenir ma tête autour de la façon de passer cette 2ème paramètre. Quelqu'un peut-il me dire comment?

Idéalement, je veux instancier Util comme:

Util myUtil = getComponent.getUtil(txtAddress.getText());

121voto

Semafoor Points 883

J'ai eu la même question que toi quand j'ai commencé à regarder dans Dague 2 il y a quelques semaines. J'ai trouvé des informations sur ce (et la plupart des autres Poignard 2-questions connexes) difficile à trouver, donc j'espère que cela aide!

La plupart des réponses de base est que vous ne pouvez pas. Ce que vous recherchez est quelque chose qui s'appelle l'injection assistée, et il ne fait pas partie de la Dague 2. Certains autres d'injection de dépendance (DI) cadres, tels que Guice, offrent cette fonctionnalité, vous devriez regarder dans ceux. Bien sûr, il y a toujours des moyens de faire ce que vous voulez faire à l'aide de la Dague à 2.

Usines, usines, usines

La façon habituelle de faire ce que vous voulez faire en combinaison avec DI est en utilisant le modèle de Fabrique. En gros, vous créez un produit injectable usine de classe qui prend des paramètres d'exécution tels que l' address comme arguments à la création d'un objet de méthodes qu'il propose.

Dans votre cas, vous auriez besoin d'un UtilFactory en ce qui Dague 2 injecte un Validator sur instantation et qui propose une méthode create(String address) qui crée des instances de Util. UtilFactory devez conserver une référence à l'instance injectée de Validator alors qu'il a tout ce qu'il faut pour créer une instance d' Util dans la create méthode.

Tordre le code pour beaucoup de ces usines peuvent être lourdes. Vous devriez certainement jeter un oeil à AutoFactory, ce qui facilite un peu la tâche. Guice de l'injection assistée semble fonctionner assez similaire à la Dague 2 + AutoFactory (mais avec encore plus beau sucre syntaxique).

D'autres modules / composants

Je doute que ce est quelque chose que vous aimeriez faire dans ce cas, mais vous pourrait il suffit de créer un module qui fournit l'adresse (et instancier un nouveau composant). Vous n'avez pas à créer un nouveau @Module de classe pour chaque adresse. Au lieu de cela, vous pouvez simplement passer l'adresse en argument du constructeur du module. Vous pouvez utiliser le @BindsInstance-annotation comme suggéré par teano pour obtenir un résultat semblable.

Je ne suis pas sûr si ce est un anti-modèle ou non. Pour moi, cela semble acceptable route dans certains cas, mais seulement quand vous êtes en fait en utilisant le même par exemple l'adresse pour l'initialisation de "nombreux" objets. Vous ne voulez certainement pas à instancier un nouveau composant et un nouveau modèle pour chaque objet nécessite l'injection. Il n'est pas efficace, et si vous ne faites pas attention, vous allez vous retrouver avec plus de code réutilisable, que sans coup de Poignard.

Ne pas (toujours) l'utilisation DI: les Injectables contre newables

Quelque chose qui a été extrêmement utile pour moi lors de l'apprentissage sur DI cadres a été la prise de conscience que l'aide d'un DI-cadre ne veut pas dire que vous devez DI initialiser tous de vos objets. Comme une règle du pouce: injecter des objets que vous savez au moment de la compilation, et qui ont statique des relations avec d'autres objets; ne pas injecter les informations d'exécution.

Je pense que c' est un bon post sur le sujet. Il introduit le concept de "newables" et "les injectables'.

  • Les Injectables sont les classes près de la racine de votre DI graphique. Les Instances de ces classes sont le genre d'objets que vous attendez de vos DI-cadre à fournir et à injecter. Gestionnaire ou à un service de type objets sont des exemples typiques de produits injectables.
  • Newables sont des objets à la lisière de votre DI graphique, ou qui ne sont même pas vraiment partie de votre DI graphique. Integer, Address etc. sont des exemples de newables.

Globalement, newables sont des objets passifs, et il n'y a pas de point de l'injection ou de se moquer d'eux. Ils contiennent généralement les "données" qui est dans votre application et qui est disponible uniquement lors de l'exécution (par exemple, votre adresse). Newables ne devrait pas garder de références à des produits injectables, ou vice-versa (quelque chose de l'auteur du post se réfère à "l'injectable/newable-séparation").

En réalité, j'ai constaté que ce n'est pas toujours facile ou possible de faire une distinction claire entre les injections et les newables. Encore, je pense qu'ils sont beaux concepts à utiliser dans le cadre de votre processus de pensée. Certainement réfléchir à deux fois avant d'en ajouter encore une autre usine à votre projet!

Dans votre cas, je pense qu'il serait judicieux de traiter Util comme un produit injectable et la traiter comme une newable. Cela signifie que l'adresse ne doit pas être une partie de l' Util classe. Si vous souhaitez utiliser l'instance de Util pour, par exemple, la validation et le... les adresses, il suffit de passer l'adresse que vous souhaitez valider comme un argument de la validation et de la... de la méthode.

20voto

teano Points 171

Vous pouvez modifier le composant générateur d'injecter des instances. Voir: https://google.github.io/dagger/users-guide#binding-instances

Dans votre cas, vous pouvez appeler:

Util myUtil = DaggerMyComponent.builder().withAddress(txtAddress.getText()).build().getComponent().getUtil();

si MyComponent est défini comme:

@Component(modules = UtilModule.class)
interface MyComponent{

    MyComponent getComponent();

    @Component.Builder
    interface Builder {

        @BindsInstance Builder withAddress(@Address String address); //bind address instance

        MyComponent build();

    }
}

Et UtilModule:

@Module
class UtilModule{

    @Provides
    Util getUtil(Validator validator, @Address String address){ //inject address instance
        return new Util(validator, address);
    }

}

Validateur doit être fourni avec un @Inject annoté constructeur ou un @Fournit une méthode annotée dans un module de classe a réussi à MyComponent des modules dans le @Composant d'annotation.

3voto

Francis Shi Points 254

quand initier le module, vous pouvez passer certains paramètres comme ceci:

 public NetServiceModule(String baseUrl, boolean isLogEnabled, CookieJar cookieJar) {
    this.mBaseUrl = baseUrl;
    this.mIsLogEnabled = isLogEnabled;
    this.mCookieJar = cookieJar;
}
 

Et puis récupérez le composant dans "Container Class":

 NetServiceComponent component = DaggerNetServiceComponent.builder()
            .netServiceModule(new NetServiceModule(baseUrl, mIsLogEnabled, cookieJar))
            .build();
    component.inject(this);
 

Avec Fournit une méthode pour fournir une injection qui génère par certains paramètres si elle a besoin de:

 @Provides
Retrofit provideRetrofit(OkHttpClient httpClient, GsonConverterFactory gsonConverterFactory, NetCallAdapterFactory netCallAdapterFactory) {

    return new Retrofit.Builder()
            .client(httpClient)
            .baseUrl(mBaseUrl)
            .addConverterFactory(gsonConverterFactory)
            .addCallAdapterFactory(netCallAdapterFactory)
            .build();
}
 

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X