Подтвердить что ты не робот

кинжал 2 круговая зависимость

В проекте, над которым я работаю, у меня есть 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();

Но я не уверен, что это правильный курс действий - есть ли лучший путь?

4b9b3361

Ответ 1

Я не подозреваю, что OP понравится, так как вам нужен способ сделать что-то "неправильным", работать "правильно". На самом деле это не так. Всякий раз, когда вы сталкиваетесь с циклической зависимостью, "правильным" решением является рефакторинг для удаления этой зависимости.

В вашем случае WorkExecutor является синглом, поэтому он, вероятно, должен оставаться как есть. Затем ExecutionServicesImpl необходимо изменить, чтобы удалить зависимость от WorkExecutor. Не зная специфики кода, нельзя сказать слишком много. Однако, если ExecutionService не зависит от него, "рабочий" - это сокращение связи и, вероятно, очень хорошая вещь в долгосрочной перспективе.

Ответ 2

почему ExecutionServicesImpl зависит от WorkExecutor?

Покажите больше ядро, ExecutionServices тоже звучит как Singleton, почему он зависит от того, как правильно работать?

WorkExecutor звучит как нечто, что вы можете передать в ExecutionService, поскольку WorkExecutor будет впрыснут где-то в другом месте, возможно, тот, который использует Service.

Я не знаю, показываю больше кода и, вероятно, ответ, он выглядит сложным.

Ответ 3

Я столкнулся с ситуацией в проекте Swing, где сторонний объект зависел от JFrame, а JFrame зависел от стороннего объекта для создания своей области содержимого. Я придумал это решение, но в конечном итоге решил закрыть цикл в моем основном методе после построения графа объектов. В принципе, я создал двух названных поставщиков 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;
}

Чтобы это сработало, второй провайдер должен был использоваться хотя бы один раз. Вы можете убедиться в этом, добавив к первому, как описано в этом ответе пакет-частный квалификатор.