I am building a system with a large number of classes all of which share common functionality but also have smaller amounts of functionality shared by overlapping subsets of classes. What are the best way/s of implementing this?
As an example I will take SVG (I am implementing a subset for chemistry), although I have examples in other areas such as chemistry. This, and related problems, have clear, immutable, design specifications in XML Schemas.
There is a base class SVGElement
which has much common functionality. All other classes (SVGLine
, SVGCircle
...) are directly derived from this. They then have other functionality defined in the spec (through XMLattributes) such as:
- x
- width
- fill
- opacity
and many more. Note that the design is given - we cannot recreate new elements or attributes. For example SVG has both circle
and ellipse
(the classic problem) and one uses r(adius)
, the other width
and height
. Where subclasses share common functionality they can use common code.
There seem to be the following solutions:
hasWidth
) and create a delegate subclass for each (WidthImplementor
). Each class then has a list of Interfaces (maybe up to 20 for each SVG attribute) and there maybe 100-200 such implementors.and maybe others.
I'm aware that there are other SO questions on how to design the abstraction but here that is largely fixed. I'd like a general solution, not just an SVG one (which is used as an understandable example). For example Chemical Markup Language (CML) also has 100-200 subclasses and 100-200 attributes, spread in a sparse matrix.
You might want to consider implementing the 'common' functionality as 'Decorators'. You can then apply these decorators at runtime, as you parse your SVG elements.
abstract class SVGElement {
public void draw (void) {
return;
}
public boolean hasWidth (void) {
return false;
}
public boolean hasHeight (void) {
return false;
}
// ..
}
// An abstract decorator
abstract class SVGElementDecorator extends SVGElement {
protected SVGElement decoratedElement;
public SVGElementDecorator (SVGElement decoratedElement) {
this.decoratedElement = decoratedElement;
}
public void draw (void) {
decoratedElement.draw ();
}
}
// Decorator to add width functionality
class WidthDecorator extends SVGElementDecorator {
private double width;
public WidthAndHeightDecorator (SVGElement decoratedElement) {
super(decoratedElement);
}
// override base class implementation
public boolean hasWidth (void) {
return true;
}
// implement a new method
public double getWidth (void) {
return width;
}
public void draw (void) {
// do special stuff for width
// ..
// then draw the rest
decoratedElement.draw ();
}
}
Then, when you see an ellipse:
SVGElement ellipse = new SVGElement();
ellipse = new WidthDecorator (ellipse);
ellipse = new HeightDecorator (ellipse);