dague 2 dépendance circulaire

Dans un projet sur lequel je travaille, j’ai 2 classes qui dépendent fortement l’une de l’autre:

@Singleton class WorkExecutor { @Inject Provider services; ... public void execute(Work w){ w.execute(services.get()); ... } ... } class ExecutionServicesImpl implements ExecutionServices { @Inject WorkExecutor executor; ... } 

L’idée est que lors de l’exécution d’un travail, celui-ci a access à plusieurs services, dont l’exécutant lui-même, de sorte qu’un travail puisse exécuter des sous-travaux.

Comme on peut le constater, il existe une dépendance circulaire, mais que j’ai eu beaucoup de mal à briser.

Le principal problème est que WorkExecutor n’a pas réellement besoin d’une instance d’object ExecutionServices au moment de la construction du graphe, mais uniquement d’un fournisseur à utiliser ultérieurement. Malheureusement, Dagger ne sait pas que WorkExecutor n’appellera pas le fournisseur ExecutionServices à partir du constructeur de la classe, de sorte qu’il devine qu’ExecutionServices dépend de WorkExecutor et inversement.

Une solution possible que j’ai trouvée consiste à définir un module et un composant de la manière suivante:

 interface DelayedProvider extends Provider{} @Module class AppModule { Provider delayedProvider = null; @Provides DelayedProvider provideDelayed() { return () -> delayedProvider.get(); } @Provides @Named("late-binding-conf") Void latebindingConf(Provider eager){ this.delayedProvider = eager; return null; //notice we returning Void and not void } } @Component(modules=AppModule.class) interface AppComponent { App app(); @Named("late-binding-conf") Void configureLateBinding(); } 

et puis je modifie les classes d’origine pour qu’elles soient:

 @Singleton class WorkExecutor { @Inject DelayedProvider services; ... public void execute(Work w){ w.execute(services.get()); ... } ... } class ExecutionServicesImpl implements ExecutionServices { @Inject WorkExecutor executor; ... } 

Et puis, pour créer mon application, je dois faire:

 AppComponent acomp = DaggerAppComponent.create(); App = acomp.app(); acomp.configureLateBinding(); 

Mais je ne suis pas sûr que ce soit la bonne marche à suivre – existe-t-il un meilleur moyen?

Je ne pense pas que OP va aimer ça, car vous voulez un moyen de faire quelque chose de «mal», de «bien travailler». Cela ne peut pas vraiment être. Chaque fois que vous rencontrez une dépendance circulaire, la “bonne” solution consiste à refactoriser pour supprimer cette dépendance.

Dans votre cas, WorkExecutor est un singleton, il doit donc probablement restr tel quel. ExecutionServicesImpl doit ensuite être modifié pour supprimer la dépendance sur WorkExecutor. Sans connaître les détails du code, on ne peut pas en dire plus. Cependant, avoir un service d’exécution indépendant de son “travailleur” est un couplage réduit et est probablement une très bonne chose à long terme.

pourquoi ExecutionServicesImpl dépend-il de WorkExecutor?

Montrer plus de base, ExecutionServices ressemble à un Singleton aussi, pourquoi cela dépend-il de fonctionner correctement?

WorkExecutor ressemble à quelque chose que vous pouvez transmettre à ExecutionService car WorkExecutor sera injecté ailleurs, peut-être celui qui utilise le Service .

Je ne sais pas, montrer plus de code et c’est probablement la réponse, ça a l’air complexe.

J’ai rencontré un JFrame dans un projet Swing où un object tiers dépendait d’un JFrame , et le JFrame dépendait de l’object tiers pour produire son volet de contenu. Je suis venu avec cette solution, mais j’ai finalement décidé de fermer la boucle dans ma méthode principale après la construction du graphe d’object. Fondamentalement, j’ai créé deux fournisseurs nommés JFrame , le second dépendant du premier et renvoyant la même instance:

 @Provides @Singleton @Named("DO_NOT_INJECT") JFrame mainWindowIncomplete() { return new JFrame(); // after setting it up } @Provides @Singleton CControl dockControl(@Named("DO_NOT_INJECT") JFrame mainWindow) { return new CControl(mainWindow); } @Provides @Singleton @Named("MAIN_WINDOW") JFrame mainWindow(@Named("DO_NOT_INJECT") JFrame mainWindow, CControl dockControl) { mainWindow.add(dockControl.getContentArea()); return mainWindow; } 

Pour que cela fonctionne, le deuxième fournisseur devra être utilisé au moins une fois. Vous pouvez vous en assurer en ajoutant un qualificatif paquet-privé au premier, comme décrit dans cette réponse .