tags:

views:

152

answers:

7

I`m currently working out the design for simple graphic editor, who support trivial operations for two-dimensional and three-d shapes.
The point is, I want to render prototype of these shapes, as MsPaint does. And at the moment it is rendering I need to store somewhere pixels from the canvas which get covered by prototype, just in case when prototype changes to restore their state on the canvas. So, I want all my shapes to support this kind of buffering (Graphic Operation is the base class for all rendering operations):

public abstract class Shape: GraphicOperation {
        protected List<SomePoint> backup;
        public Shape(Color c): base(c) { }

        public Color FigureColor {
            get { return color; }
            protected set { color = value; }
        }
        public abstract void renderPrototype(Bitmap canvasToDrawOn);
}


The main idea is that in terms of OO design it would be great to provide the support of the buffer on base class (Shape) level, I mean for TwoDShape and ThreeDShape classes this list must be initialized in different way - for TwoDShape with TwoDPoint instances, and for ThreeDShape with ThreeDPoint instances.
But to do this, SomePoint must be base class for both two-dimensional point and three-dimensional point classes. Is it acceptable in terms of OO to derive both these classes from single base class?
May be there are too many words, but I just wanted the problem to be clear for everyone.
Edit: btw, is this the good idea to derive point classes from their king of shapes? I personally see no other options, but may be it would be better if I derive it directly from shape? Now it is:

public abstract class TwoDShape : Shape {
        protected List<SomePoint> backup;
        public TwoDShape(Color c) : base(c) { }
    }
public class TwoDPoint: TwoDShape {
        //...
}

and the same is for ThreeDPoint.

A: 

In games the 2d point and 3d pointtypically don't inherit from eachother, but to be honest I can't think of any fundamental reason for this. So yeah it should be fine, as for naming they are typically vector2 and vector3 so they can hold dimensions, points and colors, all in the same object.

Robert Gould
+2  A: 

I don't see any reason not to. But if the only difference between Shapes is in the type of Points they contain, why not make the Shape class itself a template, or a "generic" in Java parlance?

oggy
it`s C# actually, but I don`t think making template classes is what I will get benefit from in terms of flexible OO design
chester89
A: 

Couldn't you make ThreeDimensionalPoint derive itself from TwoDimensionalPoint?

public class ThreeDimensionalPoint : TwoDimensionalPoint
{

}

I could see a lot of reusable code (methods/properties) in 2D that will be the same in 3D... And you might want to look at a 3D from a 2d standpoint and then all you would need to do is cast it. Just my thougts...

J.13.L
Hm, I can't think of much that'd be reusable between the two. Care to elaborate? A 3d shape is *not* a 2d shape. It can be projected onto a 2d plane, but it is not a two dimensional shape.
jalf
I can, but it will be not so reusable decision, and the most important thing is I must bring this list with restored points down one level in hierarchy - so it`s not so flexible either
chester89
jalf - I agree, in terms of OO derive one from another is not the better idea
chester89
2d point x,y,3d point x,y,z I don't know I definitely not a graphics/games expert...
J.13.L
I agree with jalf. If anything, a 2d point should inherit from a 3d point because a 2d point IS-A 3d point. Good sub-classes generally constrain the concept the base class represents, not extend it (e.g square inherits from rectangle inherits from polygon inherits from shape).
timday
timday: square only inherits from rectangle if both are immutable -- that's the *other* classic example of "things that look like simple inheritance but are actually full of gotchas". :-)
Ken
+2  A: 

What do 2d and 3d points have in common, exactly? If it is just "when drawing them onto a surface, they replace pixels which should be backed up", that sounds more like composition than inheritance to me. Both 2d and 3d shapes have a buffer of pixels they're displacing.

Anyway, can you always use both 2d and 3d shapes when the base class is expected?

If so, give them a common base class. Otherwise don't.

Base your class design on how the objects are used. If they are used in a context where they have a common base, implement it as a common base class. If it is just a matter of "I can't be bothered adding a data member to both", inheritance is probably not the right tool.

jalf
but all my shapes - both point classes included - are derived from Shape - so they`ll got this list as inherited member.
chester89
+1  A: 

There's an entirely general rule of OOD that states: Favor composition over inheritance.

What this means in practice is that you shouldn't use inheritance for code reuse. It is still legal and valid to use inheritance if the motiviation for doing so is to take advantage of polymorphism.

In your case, if you can manage to write the abstract class/interface SomePoint in such a way that you can deal only with it on that level, and never need to downcast, you should definitely go for it.

If, on the other hand, you find that you need to downcast instances to, say, TwoDPoint or ThreeDPoint, then you gain nothing from inheritance, and you would be breaking the Liskov Substitution Principle. If this is the case, you should consider a design where you can implement reuse without resorting to inheritance - perhaps using a Service or a Strategy.

Mark Seemann
+2  A: 

I can't think of any way to do this that doesn't violate the Liskov substitution principle in some subtle (or not-so-subtle) way. This is a classic example of why not to go crazy with inheritance, just because two classes share some fields, e.g., here or here.

In the end, even if you make it work, it'll probably have so many gotchas and restrictions on the methods you do write, I can't imagine it'll be a net win.

Ken
Very good answer, I think this is what I wanted to say but phrased much better.
Bill K
A: 

A simpler solution, and one that will avoid some subtle design issues is to just treat all points as 3D and simply ignore the Z coordinate if you're working within a 2D context. You can consider a 2D shape to simply be a 3D shape that happens to lie on a flat plane.

munificent