views:

412

answers:

4

Consider this simple example.

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

In this example instance A knows about instance B, and instance B knows about instance A.

My question is: how to instantiate instance A with Guice, i.e. how to make Guice take care of this complex circle dependencies?

A: 

The answer is that you can not use dependency injection framework until 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)

Yury Litvinov
I think you need to reread the bold part :)
Lasse V. Karlsen
Agreed with Yury. Circular dependencies with DI cause pain.
Jesse Wilson
+1  A: 

To answer your first question "how to instantiate instance A with Guice": you can simply add @Inject to the constructor:

class A {
   private final B b;

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

This works because the API for creating A doesn't have a circular dependency. Guice will just use the A constructor any time it needs to create or inject an A object.

If your question is how to use Guice to create an object where the API for creating the object has a circular dependency, see this blog post by Misko Hevery (as mentioned in Yury's answer).

NamshubWriter
You don't even need @Inject for a no-args constructor.
ColinD
That's the exact same code as Yury's. The @Inject takes no effect and the code doesn't decouble A from B, which is the whole point of guice.
nes1983
Yes, this is the same code as the OP wrote (with an @Inject to make it clearer that the constructor is used by Guice). The point is that there isn't a circular dependency in the API, so Guice can create A.Whether this design is a good design is a different question. You can see some of my concerns in my comments to the OP, and the OP agreed that circular dependencies are a bad idea.
NamshubWriter
+3  A: 

Your example is not an issue at all, since you're constructing B directly. But if you want to both A and B to be created by Guice, one or both should be an interface. You can do:

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;
   }
   // ...
}

Even if AImpl and BImpl are scoped as singletons, Guice can handle this injection (by way of a proxy). This works in a simple case like this at any rate... I imagine there could be more complex circular dependencies that it couldn't handle. Anyway, eliminating circular dependencies would be preferable, of course.

ColinD
This really works ONLY for singletons. What about the general case?
nes1983
+1  A: 

I think that NamshubWriter's proposal isn't very guicy. I think that in Guice, a constructor should do exactly one thing: assign parameters into fields. If there's anything else you need to do, put it into a factory or a provider.

In this case, we'll want a provider for A. The provider could directly call new B(), but then we'd directly couple A to B, which is what we tried to avoid in the first place. So we indirect the creation of B over a factory, which guice can provide for us via assistedInject. This code runs and compiles fine, and completely decouples A and B.

In a realistic scenario, you'd need to hide A and B behind interfaces to take advantage of the separation.

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

I made an extended version of the circular dependency injection in Guice where A and B are hidden behind interfaces.

nes1983