Для большинства определений "повторного использования" повторное использование кода является мифом, по крайней мере, по моему опыту. Можно ли сказать, что у меня есть некоторые шрамы от этого?:-)
повторным использованием, я не означаю брать существующие исходные файлы и бить их в представление до нового компонента, или сервис выпадает. Я означаю брать определенный компонент или сервис и снова использовать его без изменения.
я думаю, что первый шаг должен вовлечь себя в мышление, что он собирается взять по крайней мере 3 повторения для создания допускающего повторное использование компонента. Почему 3? Поскольку в первый раз Вы пытаетесь снова использовать компонент, Вы всегда обнаруживаете что-то, что он не может обработать. Таким образом необходимо изменить его. Это происходит пару раз, пока наконец у Вас нет компонента, который, по крайней мере, кажется, является допускающим повторное использование.
другой подход должен сделать дорогой перспективный дизайн. Но тогда стоимость все оплачиваема авансом, и преимущества (могут) появиться некоторое время в будущем. Если Ваш босс настоит, что текущий график проектных работ всегда доминирует, то этот подход не будет работать.
Чтобы ответить на ваш первый вопрос «как создать экземпляр A с помощью Guice»: вы можете просто добавить @Inject
в конструктор:
class A {
private final B b;
@Inject
A() {
this.b = new B(this);
}
}
Это работает, потому что API для создание A
не имеет циклической зависимости. Guice будет просто использовать конструктор A
каждый раз, когда ему потребуется создать или внедрить объект A
.
Если ваш вопрос заключается в том, как использовать Guice для создания объекта, в котором API для создание объекта имеет круговую зависимость, см. это сообщение в блоге Миско Хевери (как упоминалось в ответе Юрия).
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)
Ваш пример не представляет проблемы, поскольку вы строите 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 может обработать эту инъекцию (через прокси). Во всяком случае, это работает в таком простом случае ... Я полагаю, могут быть более сложные циклические зависимости, с которыми он не может справиться. В любом случае, конечно, предпочтительнее было бы исключить циклические зависимости.
Я думаю, что предложение 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 скрыты за интерфейсами.