For the following model, would you prefer inheritance or composition:
canvas
that each represent a data objectEllipses
represent States
, Lines
represent connections/transitions
between them. The object representation itself will never change, ie a State
will always be shown by a ellipse
. But the way an ellipse
is drawn should differ, eg for selection it should have a different color, while dragging it should maybe have an alpha channel, etc.From the design point of view, an ellipse
is not a state
, and a line
is not a transition
. Anyhow it would be suitable to combine both objects for being able to collect them in a List<Shape>
and execute shape.draw()
on every object.
Now 2 design models are possible, whereas I would consider 2 classes to be always the same:
interface Shape {
void draw();
}
abstract class Figure implements Shape {
//basic vars like start and end coordinates
int x0, y0, x1, y1;
}
Inheritance:
abstract class State extends Figure {
String name;
}
class Rectangle extends State {
@Override void draw();
}
class Line extends Figure;
class Transition extends Line
Although from design point of view a rectangle
is not a State
, and a state
is not a figure
, regarding the drawing context this might be viable. Because I could inherit most of the stuff needed for handling the shapes
, drawing etc.
Or Composition:
abstract class State {
String name;
}
class Rectangle extends Figure {
private State state;
@Override void draw();
}
class Line extends Figure {
private Transition transition;
@Override void draw();
}
So Rectangle
+ Line
would be wrappers for my objects.
Should a Rectangle and Line rather extends State and Transition, or contain it?
Or, maybe there is also a 3rd design option that I did not see. Looking forward to your ideas.
So, here's my ideas, but with most design question, there's rarely ever one "correct" answer.
Like you said, a State is not a Rectangle, and a Transition is not a Line. We can draw a Line and/or a Rectangle, and there may be some advantage to treating them similarly. So I can translate those statements into a simple design:
public interface Drawable
{
public void draw();
public Position getPos();
}
public class Rectangle implements Drawable ...
public class Line implements Drawable ...
Now, State's and Transition's can be represented by these Drawables. Have you ever heard of Single Responsibility Principle? It's basically exactly what it sounds like, an Object should be responsible for doing one "thing". Rectangles and Lines know how to draw themselves. States and Transitions presumably have some other job to do in your system.
public interface Figure
{
public Drawable getDrawable();
}
public class State implements Figure
{
private Rectangle rect;
public Drawable getDrawable() { return rect; }
//... State's real "work" below
}
public class Transition implements Figure
{
private Line line;
// you get the idea
}
On a small/simple system, the advantages of SRP may be lost, but the idea is that we're separating rendering from the other system logic. The more we separate functionality, the less brittle the system will be when the time for changes come.