Используя Guice с круговыми зависимостями

Для большинства определений "повторного использования" повторное использование кода является мифом, по крайней мере, по моему опыту. Можно ли сказать, что у меня есть некоторые шрамы от этого?:-)

повторным использованием, я не означаю брать существующие исходные файлы и бить их в представление до нового компонента, или сервис выпадает. Я означаю брать определенный компонент или сервис и снова использовать его без изменения.

я думаю, что первый шаг должен вовлечь себя в мышление, что он собирается взять по крайней мере 3 повторения для создания допускающего повторное использование компонента. Почему 3? Поскольку в первый раз Вы пытаетесь снова использовать компонент, Вы всегда обнаруживаете что-то, что он не может обработать. Таким образом необходимо изменить его. Это происходит пару раз, пока наконец у Вас нет компонента, который, по крайней мере, кажется, является допускающим повторное использование.

другой подход должен сделать дорогой перспективный дизайн. Но тогда стоимость все оплачиваема авансом, и преимущества (могут) появиться некоторое время в будущем. Если Ваш босс настоит, что текущий график проектных работ всегда доминирует, то этот подход не будет работать.

9
задан Yury Litvinov 2 December 2011 в 19:51
поделиться

4 ответа

Чтобы ответить на ваш первый вопрос «как создать экземпляр A с помощью Guice»: вы можете просто добавить @Inject в конструктор:

class A {
   private final B b;

   @Inject
   A() {
       this.b = new B(this);
   }
}

Это работает, потому что API для создание A не имеет циклической зависимости. Guice будет просто использовать конструктор A каждый раз, когда ему потребуется создать или внедрить объект A .

Если ваш вопрос заключается в том, как использовать Guice для создания объекта, в котором API для создание объекта имеет круговую зависимость, см. это сообщение в блоге Миско Хевери (как упоминалось в ответе Юрия).

4
ответ дан 4 December 2019 в 10:05
поделиться

The answer is that you should not use a dependency injection framework while you have circular dependences in your code.

So, you have to refactor you code beforehand. As far as I know, there are two solutions for tightly coupled classes: either merge two classes into one or introduce new class and move common logic into it (for detail look here)

4
ответ дан 4 December 2019 в 10:05
поделиться

Ваш пример не представляет проблемы, поскольку вы строите B напрямую. Но если вы хотите, чтобы и A, и B были созданы Guice, один или оба должны быть интерфейсом. Вы можете:

public interface A { /* skipping methods */ }
public interface B { /* skipping methods */ }

public class AImpl implements A {
   private final B b;

   @Inject
   public AImpl(B b) {
      this.b = b;
   }
   // ...
}

public class BImpl implements B {
   private final A a;

   @Inject
   public BImpl(A a) {
      this.a = a;
   }
   // ...
}

Даже если AImpl и BImpl определены как синглтоны, Guice может обработать эту инъекцию (через прокси). Во всяком случае, это работает в таком простом случае ... Я полагаю, могут быть более сложные циклические зависимости, с которыми он не может справиться. В любом случае, конечно, предпочтительнее было бы исключить циклические зависимости.

10
ответ дан 4 December 2019 в 10:05
поделиться

Я думаю, что предложение NamshubWriter'а не очень гуисовое. Я думаю, что в Guice конструктор должен делать только одну вещь: назначать параметры в поля. Если вам нужно сделать что-то еще, поместите это в фабрику или провайдер.

В данном случае нам нужен провайдер для A. Провайдер мог бы напрямую вызвать new B(), но тогда мы бы напрямую соединили A с B, чего мы пытались избежать в первую очередь. Поэтому мы перенаправляем создание B на фабрику, которую guice может предоставить нам через assistedInject. Этот код работает и компилируется нормально, и полностью развязывает A и B.

В реалистичном сценарии вам пришлось бы спрятать A и B за интерфейсами, чтобы воспользоваться преимуществами разделения.

import com.google.inject.AbstractModule;
import com.google.inject.Guice;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.assistedinject.Assisted;
import com.google.inject.assistedinject.FactoryProvider;

public class Try {
    public static void main(String[] args) {
        System.out.println(
                Guice.createInjector(new MyModule()).getInstance(A.class)
        );
    }
}

class MyModule extends AbstractModule {
    public void configure() {
        bind(A.class).toProvider(AProvider.class);
        bind(IBFactory.class).toProvider(
                FactoryProvider.newFactory(IBFactory.class, B.class));
    }
}

class A {
    B b;

    public void setB(B b) {
        this.b = b;     
    }
}

class B {
    A a;

    @Inject
    B(@Assisted A a) {
        this.a = a;
    }
}

class AProvider implements Provider<A> {

    private final IBFactory bFactory;

    @Inject
    AProvider(IBFactory bFactory) {
        this.bFactory = bFactory;
    }

    public A get() {
        A a = new A();
        a.setB(bFactory.create(a));
        return a;
    }
}

interface IBFactory {
    public B create(A a);
}

Я сделал расширенную версию инъекции круговой зависимости в Guice, где A и B скрыты за интерфейсами.

3
ответ дан 4 December 2019 в 10:05
поделиться
Другие вопросы по тегам:

Похожие вопросы: