Is it a good idea to split each graphic shape into two parts - its geometry and rendering components?
I am writing a simple graphics editor for a university project using C #.
I created a hierarchy where every shape type - circles, rectangles, ellipses, ... - inherits from the base Shape class, it contains the shape's color as a protected field.
public abstract class Shape {
protected Color color;
public Shape(Color c) {
color = c;
}
public Color FigureColor {
get { return color; }
protected set { color = value; }
}
public abstract void render(Bitmap canvasToDrawOn);
public abstract void unrender(Bitmap canvasToDrawOn);
public abstract void renderPrototype(Bitmap canvasToDrawOn);
}
The idea is that I want each type of shape to encapsulate its geometric part, for example:
public class TwoDPoint: TwoDShape {
Point2DGeometry point;
public TwoDPoint(Color c, Point2DGeometry p): base(c) {
point = new Point2DGeometry(p);
}
public struct Point2DGeometry {
Int32 x;
Int32 y;
public Point2DGeometry(Int32 X, Int32 Y) {
x = X;
y = Y;
}
public Point2DGeometry(Point2DGeometry rhs) {
x = rhs.Abscissa;
y = rhs.Ordinate;
}
public Int32 Abscissa {
get { return x; }
private set { x = value; }
}
public Int32 Ordinate {
get { return y; }
private set { y = value; }
}
}
The Ellipse class will encapsulate EllipseGeometry, etc.
Is this a good solution in terms of OO design, and if so maybe it's a better idea to create a parallel hierarchy of these types just for geometry?
a source to share
I would say it depends. Are you going to reuse geometry objects outside of your shape objects, or share geometry objects as Flyweights? If not, you might be overcomplicating your class hierarchy.
Looking back at your code, you might also consider the following:
-
Having some kind of naming convention between your private fields and your public properties. It doesn't make sense to involve Abscissa / Ordinate when your personal variables are x / y and your constructor accepts X / Y. Ditto Color and FigureColor in the form.
-
In Shape, why is the "color" protection protected when the "FigureColor" also has a protected setter? In most APIs (and you can think of your abstract class as an API to its derived classes), you will want to minimize the confusion about using members when changing state. In this particular example, I will most likely make the "colored" private.
-
Watch out for "inner classes" in the class hierarchy that don't add any meaning. For example, you have an abstract Shape class as well as a TwoDShape appearance. What's the difference between them? Do you have support for 3 dimensions? Is your inheritance tree more like a stick?
HTH, and good luck with your project.
a source to share