18

在我正在处理的一个项目中,我有 2 个高度相互依赖的类:

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

class ExecutionServicesImpl implements ExecutionServices {
    @Inject WorkExecutor executor;
    ...
}

这个想法是,当执行一项工作时,该工作可以访问多个服务 - 其中一个是执行者本身,因此一个工作将能够执行子工作。

可以看出,这里有一个循环依赖,但我发现它很难打破。

主要问题是 WorkExecutor 在构建图时实际上不需要 ExecutionServices 对象的实例,而只需要稍后使用的提供程序。遗憾的是,Dagger 不知道 WorkExecutor 不会从类的构造函数中调用 ExecutionServices 提供程序,因此它猜测 ExecutionServices 依赖于 WorkExecutor,反之亦然。

我发现的一种可能的解决方案是通过以下方式定义模块和组件:

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();
}

然后我将原始类修改为:

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

class ExecutionServicesImpl implements ExecutionServices {
    @Inject WorkExecutor executor;
    ...
}

然后为了创建我的应用程序,我必须这样做:

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

但我不确定这是正确的做法 - 有没有更好的方法?

4

3 回答 3

2

我不怀疑 OP 会喜欢这样,因为您想要一种方法来制造“错误”的东西,“正确”地工作。这不可能。每当您遇到循环依赖时,“正确”的解决方案是重构以删除该依赖。

在您的情况下, WorkExecutor 是一个单例,因此它可能需要保持原样。然后应该修改 ExecutionServicesImpl 以删除对 WorkExecutor 的依赖。在不知道代码细节的情况下,不能说太多。然而,让 ExecutionService 独立于它的“worker”会减少耦合,从长远来看可能是一件非常好的事情。

于 2016-07-27T10:57:45.490 回答
0

我在 Swing 项目中遇到了一种情况,其中第三方对象依赖于 a JFrame,并且JFrame依赖于第三方对象来生成其内容窗格。我想出了这个解决方案,但最终决定在构建对象图后在我的 main 方法中关闭循环。基本上,我创建了两个命名JFrame提供程序,第二个取决于第一个并返回相同的实例:

@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;
}

为此,必须至少使用第二个提供程序一次。您可以通过将 package-private 限定符添加到第一个限定符来确保这一点,如本答案中所述。

于 2016-11-09T19:19:32.353 回答
0

为什么 ExecutionServicesImpl 依赖于 WorkExecutor?

显示更多核心,ExecutionServices 听起来也像 Singleton,为什么要相互依赖才能正常工作?

WorkExecutor听起来你可以传递给ExecutionServiceas的东西WorkExecutor会被注入到其他地方,也许是使用Service.

我不知道,显示更多代码,可能这就是答案,它看起来很复杂。

于 2016-09-30T21:27:35.703 回答