This question stems from more concrete problem asked here
The problem I would like to discuss here seems to be a very common pattern in software engineering. Many applications make use of two-layered architecture - business layer and a UI layer on top of it. I would like to know what is the most clean OOP way of mapping business objects to their UI presenters (or views, if you like). I will be using Java to illustrate my struggles.
Say we have a graphical framework where the bussiness layer contains shapes like Circle
, Rectangle
etc. All are derived from a common interface Shape
. In the UI we have CirclePresenter
, RectanglePresenter
etc. all derived from ShapePresenter
. The question is how to map an instance of Shape
to an instance of ShapePresenter
. I would like the system to be extendible so that users of the framework can add their own shapes and presenters without the need for recompilation.
I have seen and also can imagine several approaches but all of them seem to me as hacks which destroy the beauty of good OOP:
Approach 1: Use visitors
If we have a visitor like this
interface ShapeVisitor<T> {
public T visitCircle(Circle circle);
public T visitRectangle(Rectangle rect);
}
we can use it to create presenter factories like this:
class Factory implements ShapeVisitor<ShapePresenter> {
public ShapePresenter visitCircle(Circle circle) {
return new CirclePresenter(circle);
}
...
}
This pattern does not allow for the framework to be extendible since you would need to add new methods to the visitor every time new shape is added. I was unable to come up with an extendible visitor implementation myself, so this approach seemed useless. However, here is an attempt to create reflective visitor, which claims to solve this exact problem. If this is not the hackiest hack of all Java hacks, then I do not know what is...
Approach 2: Inspired by Eclipse
Eclipse's diagramming frameworks such as GEF and GMF face this exact issue of shapes and presenters. GMF, relies on code generation where you have to specify the shapes and presentors in a different language and then the generator spits out Java classes with tons of instanceof checks. This solution does not seem very OOP clean to me, not to mention the generator limits your expresivity. GEF, on the other hand, keeps track of the shape->presenter mapping using HashMaps. When you add new presentor, you have to add an entry to the central registry which is managed by some all-encapsulating god object such as PresentationLayer
.
The GEF approach makes the most sense to me but I am still not happy with it since it adds too many mamagement code to the framework. It seems like 20% of the code focuses on the problem at hand - defining and drawing shapes, and the other 80% is there to unsure extendibility. So in this case the most of the code consists of transcendental classes like AbstractFactoryExtensionPoint
, CompositeAdapterServiceLocator
or GenericDecoratorRegistry
. I only naivly wanted to draw some rectangles and circles in an extendible way and I ended up with bunch of AbstractAdaptableCompositeRegistryServiceLocator
s which noone can understand without reading through hundred-page documentation.
Any ideas?