Как разрешить циклическую зависимость, все еще используя Dagger2?


У меня есть два класса, Foo<T> и Bar, которые зависят друг от друга, а также различные другие классы. Я использую Dagger-2 для инъекции зависимостей, но если я наивно добавляю циклическую зависимость, Dagger попадает в переполнение стека во время выполнения. Каков хороший способ рефакторинга классов, чтобы исправить это, все еще используя Dagger для внедрения всех других зависимостей и с минимальным дублированием и изменениями существующих вызовов?

3 7

3 ответа:

Самый простой выход-использовать Lazy<T> с одной стороны.

Lazy<Foo> foo;

Bar(Lazy<Foo> foo) {
    this.foo = foo;
}

// use foo.get(); when needed

После чрезмерного количества размышлений и бесед с коллегами, мы закончили тем, что сделали следующее:

class Foo<T> extends FooWithoutDep<T> {
    @Inject Foo(Bar bar, OtherDep1 dep1, OtherDep2 dep2) {
        super(dep1, dep2);
        setBarDep(bar);
    }
}

class FooWithoutDep<T> {
    //Field declarations elided
    @Inject FooWithoutDep(OtherDep1 dep1, OtherDep2 dep2) {
        //Normal constructor stuff
    }
    void setBarDep(Bar bar) { this.bar = bar; }

    //The rest of the actual logic
}

class Bar {
    //Field declarations elided
    @Inject Bar(FooWithoutDep<Thing> foo, OtherDep3 dep3) {
        this.foo = foo;
        this.foo.setBarDep(this);
        this.dep3 = dep3;
    }

    //Code that uses Foo and the other dependencies
}

Объясняя это - мы переместили фактическую логику Foo в родительский класс (FooWithoutDep), который взял циклическую зависимость в качестве настраиваемого поля, а не параметра конструктора. Тогда исходный класс просто содержал конструктор, который взял циклическую зависимость и вызвал сеттер. Другой класс, Bar, зависел от родителя (FooWithoutDep) и назывался сеттером явно, передавая себя (this). Это позволяет всем существующим ссылкам на класс оставаться неизменными, в то же время используя Dagger для внедрения всех зависимостей.

Это казалось достаточно запутанным,чтобы стоило написать здесь.

Вот как я решил ее, без родительских классов.

Класс 1: Двигатель. (в интерфейсе компонента) @Обеспечивает публичный движок myEngine (контекст context) { возврат нового движка (контекст); }

Класс 2: Части. Двигатель также нуждается в деталях экземпляра, но создание откладывается.

@Inject
public Parts(Context context, Engine engine) {
    this.context = context;
    this.engine= engine;
    engine.setParts(this);
}

Циклическая зависимость может быть достигнута, но один класс должен быть инициирован первым перед другим.

{[1] снова}, если это возможно, рефакторинг кода, чтобы избежать циклических Ди.