2013-04-14 2 views
4

Je suis nouveau à Guice et je me demandais jusqu'où je peux le prendre. J'ai une interface UserInfo avec plusieurs classes de mise en œuvre GoogleUserInfo, FacebookUserInfo, TwitterUserInfo etc. Ces classes sont créées en utilisant une usineRemplacement de l'usine abstraite avec Guice?

public class UserInfoFactory { 
    public UserInfo createFromJsonString(String jsonspec) { 
    . 
    . 
    . 

    } 
} 

La création est contrôlée par la chaîne JSON jsonspec qui contrôle qui des cours de mise en œuvre de UserInfo est revenu. Plus précisément, il existe un élément de chaîne JSON domain qui contrôle la création. La création est vraiment une fonction de la désérialisation de jsonspec en utilisant GSON.
Je me demandais s'il y avait un bon moyen de remplacer cette création par une injection de dépendance de Guice?

Répondre

6

Vous pouvez intégrer Guice en usine, mais votre code est probablement mieux comme il est dans ce cas.

C'est en fait l'une de ces usines qui ne peuvent pas être remplacées facilement, car elle doit contenir la logique pour analyser le jsonSpec et changer le type concret qu'il renvoie en fonction de cela. Disons que la version légèrement moins simplifiée de l'usine ressemble à ceci:

public class UserInfoFactory { 
    public UserInfo createFromJsonString(String jsonspec) { 
    if(getUserType(jsonSpec) == TWITTER) { 
     return new TwitterUserInfo(jsonSpec); 
    } else { /* ... */ } 
    } 

    private UserInfoType getUserType(String jsonSpec) { /* ... */ } 
} 

Cette logique doit vivre quelque part, et votre propre UserInfoFactory semble comme une maison parfaite. Toutefois, étant donné que vous utilisez new, vous ne pourrez pas injecter l'une des dépendances de TwitterUserInfo, ou les dépendances de ses dépendances - et que est le type de problème que Guice résout joliment.

Vous peut injecter TwitterUserInfo comme Provider, qui vous donnera accès au plus grand nombre TwitterUserInfo entièrement injecté des objets que vous souhaitez:

public class UserInfoFactory { 
    @Inject Provider<TwitterUserInfo> twitterUserInfoProvider; 

    public UserInfo createFromJsonString(String jsonspec) { 
    if(getUserType(jsonSpec) == TWITTER) { 
     TwitterUserInfo tui = twitterUserInfoProvider.get(); 
     tui.initFromJson(jsonSpec); 
     return tui; 
    } else { /* ... */ } 
    } 
} 

... et, bien sûr, que vous permet également d'injecter un @Twitter Provider<UserInfo> si vous avez seulement besoin de l'interface et que vous voulez faire varier la classe concrète dans le futur.Si vous voulez TwitterUserInfo accepter les paramètres du constructeur, Assisted injectionvous allez aider à créer un TwitterUserInfoFactory bien, qui l'aidera vers immutability:

public class UserInfoFactory { 
    @Inject TwitterUserInfo.Factory twitterUserInfoFactory; 

    public UserInfo createFromJsonString(String jsonspec) { 
    if(getUserType(jsonSpec) == TWITTER) { 
     return twitterUserInfoFactory.create(jsonSpec); 
    } else { /* ... */ } 
    } 
} 

// binder.install(new FactoryModuleBuilder().build(TwitterUserInfoFactory.class)); 
public class TwitterUserInfo implements UserInfo { 
    public interface Factory { 
    TwitterUserInfo create(String jsonSpec); 
    } 

    public TwitterUserInfo(@Assisted String jsonSpec, OtherDependency dep) { /* ... */ } 
} 

Une note finale:TwitterUserInfo ne probablement pas une dependencies-- cela ressemble à un objet de données pour moi - donc laisser votre classe exactement comment vous l'avez trouvé (avec new) est probablement la meilleure approche. Bien qu'il soit agréable d'avoir des interfaces et des classes découplées pour faciliter les tests, la maintenance et la compréhension ont un coût. Guice est un marteau très puissant, mais tout n'est pas vraiment un clou à marteler.

1

Vous voudrez probablement utiliser l'injection assistée.

Vous devez annoter vos constructeurs à GoogleUserInfo, FacebookUserInfo et TwitterUserInfo avec (@Assisted chaîne jsonspec) (et bien sûr @Inject)

Ensuite, vous devrez configurer votre classe d'usine

binder.install(new FactoryModuleBuilder().build(UserInfoFactory.class)); 

Ensuite, liez de manière appropriée le fournisseur Info que vous souhaitez utiliser.

I Pensez. Je suis assez nouveau pour me guérir.

+0

L'injection assistée est une bonne chose à rechercher ici mais n'est pas assez intelligente pour renvoyer différents types de béton basés sur le résultat de la classe JSON. Comme spécifié ici, Guice ne parviendra pas à fournir une implémentation pour UserInfoFactory, car il ne peut pas instancier l'interface UserInfo, et sans appeler 'FactoryModuleBuilder.implement', l'interface est tout ce que FactoryModuleBuilder peut renvoyer. –

+0

@JeffBowman - donc si vous avez dans Module.configure binder.install (new FactoryModuleBuilder(). Build (UserInfoFactory.class)); ajouter une fonction au module public Class bindUserInfo() {return GoogleUserInfo.class; } ne fonctionnerait pas? (Pardonnez-moi, j'essaie de comprendre Guice d'un point de vue différent de la normale.) J'ai une application existante avec laquelle je travaille qui utilise Guice et ce que je sais provient d'une tentative d'analyse de la base de code existante. .) –

+1

FactoryModuleBuilder fonctionne en inspectant l'interface (pas la classe concrète!) Passée dans 'build' et en fournissant une implémentation injectable pour cela. Lorsque vous appelez la méthode de cette implémentation, FactoryModuleBuilder prend le type de retour de la méthode et l'instancie, en remplissant les paramètres '@ Assisted' avec les arguments de méthode et tout le reste de l'injecteur. Si le type de retour est un _interface_, comme 'UserInfo', vous devez également appeler' implement' pour indiquer le type concret à créer. C'est pourquoi cela ne fonctionnera pas ici - le type concret est fixé au moment de la configuration. –