Immutable cyclic graph with dependency injection
Hello,
I have a structure with two classes, let's call them Car and
SteeringWheel for the example.
Car /owns/ a SteeringWheel: in production code, the lifetime of the
SteeringWheel is directly dependent on the lifetime of the Car.
In my design, the SteeringWheel object needs a reference to the Car
object (let's say, to transmit when the user honks). I see several
ways to build and initialize this class-graph, but none so far that I
think perfect.
What is the best way to create and initialise that structure?
A few of my previous ideas are:
1) Build graph dependency in constructor:
public class Car {
private final SteeringWheel wheel;
public Car() {
wheel = new SteeringWheel(this);
}
}
public class SteeringWheel {
private final Car car;
public SteeringWheel(Car car) {
this.car = car;
}
}
Cons: a) /this/ pointer leaks in constructor b) Car is not unit-
testable without SteeringWheel
2) Use a setter and a static factory
public class Car {
private SteeringWheel wheel;
public void setWheel(SteeringWheel wheel) {
this.wheel = wheel;
}
public static Car newInstance(){
Car c = new Car();
SteeringWheel s = new SteeringWheel(c);
c.setWheel(s);
return c;
}
}
public class SteeringWheel {
private final Car car;
public SteeringWheel(Car car) {
this.car = car;
}
}
Cons: a) field /wheel/ in Car cannot be made final (I like
immutability) b) I must always check if wheel was correctly set as
nothing enforces the correct construction sequence
3) The JavaBean way:
public class Car {
private SteeringWheel wheel;
public void setWheel(SteeringWheel wheel) {
this.wheel = wheel;
}
public static Car newInstance(){
Car c = new Car();
SteeringWheel s = new SteeringWheel();
c.setWheel(s);
s.setCar(c);
return c;
}
}
public class SteeringWheel {
private Car car;
public void setCar(Car car) {
this.car = car;
}
}
Cons: Same problems as 2, but with added insecurity
Are there constructs to build that graph which bundle all advantages?
ie. testable through dependency injection, immutable (for thread
safety) and guaranteing the invariants (example: wheel is not null)
Thanks for your answers
Philipp