Circular Dependency in constructors and Dependency Injection
Thursday, July 31, 2008
by Miško Hevery
So you discovered dependency injection and GUICE and you are happily refactoring and writing new tests for you code until you come across this circular reference.
Hm, dependency injection says that you need to ask for your dependencies and so the resulting code will be:
But now we have a problem, we can't instantiate this (I know GUICE can through proxy, but it is not clean and it does not help us in tests). So the real problem in situation like this is mixing of concerns. One of the two objects is hiding another object C. Either A contains C or B contains C. To find out which one it is, list all of the methods in your class A and class B and. The shorter of the two lists is your hidden Class C.
Suppose B has the shorter list. We now extract all of the methods in B which are accessing the state of hidden C methods into a new object C like this:
So you discovered dependency injection and GUICE and you are happily refactoring and writing new tests for you code until you come across this circular reference.
class A {
final B b;
A(B b){
this.b = b;
}
}
class B {
final A a;
B(){
this.a = new A(this);
}
}
+---------+ +---------+
| A |<-----| B | | | | | | |----->| |
+---------+ +---------+
Hm, dependency injection says that you need to ask for your dependencies and so the resulting code will be:
class A {
final B b;
A(B b){
this.b = b;
}
}
class B {
final A a;
B(A a){
this.a = a;
}
}
But now we have a problem, we can't instantiate this (I know GUICE can through proxy, but it is not clean and it does not help us in tests). So the real problem in situation like this is mixing of concerns. One of the two objects is hiding another object C. Either A contains C or B contains C. To find out which one it is, list all of the methods in your class A and class B and. The shorter of the two lists is your hidden Class C.
+---------+ +---------+
| A |<-----| B | | | | | +-+ | | | | +->|C| |
| |------+---->| | |
| | | +-+ |
+---------+ +---------+
Suppose B has the shorter list. We now extract all of the methods in B which are accessing the state of hidden C methods into a new object C like this:
+---------+When you go through this exercise you will realize that the C was always an object in its own right but you have never thought about it that way, so the new code is actually better OO. From testing point of view you can now test each class in isolation.
+---------+ | B |
| A |<-------------| | | | | | | | +---+ | | | |--->| C |<----| | | | +---+ +---------+ +---------+ class C { C(){ } } class A { final C c; A(C c){ this.c = c; } } class B { final A a; final C c; B(A a, C c){ this.a = a; this.c = c; } }
Could you post a small example?
ReplyDeleteWhat about circular references involving several classes. How do you apply your rule in these (more typical) cases?
ReplyDeleteI would really like to see an example too. It seems to me that the shorter of the two lists of methods is just going to be either class A or class B itself.
ReplyDeleteIs it supposed to be a list of methods in the class that reference the other object?
ReplyDeleteCan I just AOL the example request. I'm a little confused by what you mean by this.
ReplyDeleteAlso why would this be any harder to test than any other complex required dependency? Mock or stub the injected instance.
Really enjoying your posts Miško, each one makes me think :)
ReplyDeleteI've reached this from a different direction; when I've had a difficult service to test, I've split it into two cooperating services that can each be tested on their own.
ReplyDeleteHiveMind and T5 IoC are capable of co-injecting (via constructors) services; this is because what gets injected is a proxy. You'd have a problem trying to inject service implementation A into service implemenation B and vice versa at the same time, but with proxies, theres the extra abstraction.
Anyway, the mantra is: "Small is Beautiful".
An example of this (that I used on my blog):
ReplyDeleteCircular dependencies between classes can be solved by introducing a new class, e.g., if classes 'Alice' and 'Bob' has references to each other for exchanging messages, then a class 'Channel' should be introduced that they both will use to send and receive messages.
Hi Misko,
ReplyDeleteWhat kind of class would you make to resolve the circular dependency introduced by applying DI to the MVP pattern? Currently I'm manually injecting the view, inside the view's ctor, into a 'View' property in the presenter, which is ugly. Now I have to remember every time I make a new implementation of this view to set the presenter's View property. I could use a DI framework to automatically inject the property--assuming I set the View implementation to be a singleton in binding configuration (else it would try to inject a brand new view, which puts me right back where I started). There's got to be a better way, though. Maybe a better View-Presenter design?