Reputation: 2956
In a project I am working on I have 2 classes that are highly dependent on one another:
@Singleton
class WorkExecutor {
@Inject Provider<ExecutionServices> services;
...
public void execute(Work w){
w.execute(services.get());
...
}
...
}
class ExecutionServicesImpl implements ExecutionServices {
@Inject WorkExecutor executor;
...
}
The idea is that when executing a work, the work has access to several services - one of them is the executor itself so that a work will be able to execute sub works.
As one can see, there is a circular dependency here, but one that I found very hard to break.
The main problem is that the WorkExecutor does not actually need an instance of ExecutionServices object at graph-constructing time but only a provider to be used later. sadly, Dagger does not know that the WorkExecutor will not call the ExecutionServices provider from the constructor of the class so it guesses that ExecutionServices depends on WorkExecutor and vice versa.
One possible solution that I found is to define a module and component in the following way:
interface DelayedProvider<T> extends Provider<T>{}
@Module
class AppModule {
Provider<ExecutionServices> delayedProvider = null;
@Provides DelayedProvider<ExecutionServices> provideDelayed() {
return () -> delayedProvider.get();
}
@Provides @Named("late-binding-conf") Void latebindingConf(Provider<ExecutionServices> 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();
}
and then I modifies the original classes to be:
@Singleton
class WorkExecutor {
@Inject DelayedProvider<ExecutionServices> services;
...
public void execute(Work w){
w.execute(services.get());
...
}
...
}
class ExecutionServicesImpl implements ExecutionServices {
@Inject WorkExecutor executor;
...
}
And then in order to create my app I have to do:
AppComponent acomp = DaggerAppComponent.create();
App = acomp.app();
acomp.configureLateBinding();
But I am not sure this is the proper course of action - Is there a better way?
Upvotes: 18
Views: 2890
Reputation: 10298
I ran into a situation in a Swing project where a third-party object depended on a JFrame
, and the JFrame
depended on the third-party object to produce its content pane. I came up with this solution, but ultimately decided to close the loop in my main method after the object graph was constructed. Basically, I created two named JFrame
providers, the second depending on the first and returning the same 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;
}
For this to work, the second provider would have to be used at least once. You can ensure this by adding a package-private qualifier to the first one as described in this answer.
Upvotes: 0
Reputation: 1077
why does ExecutionServicesImpl depend on WorkExecutor?
Show more core, ExecutionServices sounds like a Singleton too, why does it depend on each other to work correctly?
WorkExecutor
sounds like something that you can pass to the ExecutionService
as WorkExecutor
will be injected somewhere else, maybe the one that uses the Service
.
I dont know, show more code and probably that's the answer, it looks complex.
Upvotes: 0
Reputation: 7652
I don't suspect OP will like this, as you want a way to make something 'wrong', work 'right'. That can't really be. Whenever you encounter a circular dependency, the "right" solution is to refactor to remove that dependency.
In your case, WorkExecutor is a singleton so it probably needs to remain as is. ExecutionServicesImpl should then be modified to remove the dependency on WorkExecutor. Without knowing the specifics of the code, one can't say too much more. However, having an ExecutionService be independent from it's "worker" is reduced coupling and likely a very good thing in the long run.
Upvotes: 2