The question has been haunting me for month. I beleive it's a general one for anyone who wants to expose only interface rather than classes to outside the domain layer.
Say we have two interfaces Foo and Bar, there is a one-to-many bidirectional association between them.
Code:
public interface Bar{
Foo getFoo();
}
public interface Foo{
ISet<Bar> getBars(); //returns a readonly Set
void add(Bar b);
void remove(Bar b);
}
The problem is related to the management of the association. As you can see from the interfaces, we decided to let Foo to management this one-to-many association while Bar does nto have the right to change the association.
The question is how the implementation of the Foo manage the association at the Bar side?
Take the following implementation as an example:
Code:
class FooImpl implements Foo {
private ISet<Bar> bars;
public ISet<Bar> getBars(){
return Set<Bar>.readonly(bars);
}
public void add(Bar b){
bars.add(b);
//?????? how to set the bar of f to be this?
}
}
If we don't want to add any method to the Bar interface, one way to do this is write such a BarImpl:
Code:
class BarImpl implements Bar{
private Foo foo;
public Foo getFoo(){
return foo;
}
void setFoo(Foo f){
foo = f;
}
}
Now in the FooImpl we can downcast the inputed b to BarImpl and call the setFoo function
Code:
public void add(Bar b){
bars.add(b);
((BarImpl)b).setFoo(this);
}
As we all can see, this solution is probably not accetable because 1)downcast couple the implementation of two and 2) more importantly, downcast might failed when proxy is used in the Nhibernate envrionment.
The only way that comes up to my mind to avoid this downcast is to add a package private interface, and use this interface between the implementations of Foo and Bar
Code:
interface HasFoo{
void setFoo(Foo f);
}
class BarImpl implements Bar, HasFoo{
...
void setFoo(Foo f){
foo = f;
}
}
class FooImpl implements Foo{
...
public void add(Bar b){
if (b instanceof HasFoo){
((HasFoo)b).setFoo(this);
bars.add(b);
} else
throw new Exception()
}
}
I don't think this solution is perfect since it introduced a rather "useless" interface. Does anyone know a good pattern for this?