tags:

views:

275

answers:

2

From what I`ve learned, it is no good if you frequently use downcasting in class hierarchies. I agree with that, but what are exceptions from this rule if any?
This is where my design of graphical editor shows thin: I have two hierarchies, where geometric figures hierarchy decoupled from graphic primitives one. Like this:

public class GeometricPrimitive {...}
public class RectangeGeometric: Geometric Primitive {...} 
public class GraphicPrimitive {...}
public class Rectangle: GraphicPrimitive {
 private RectangleGeometric figure;
 ...
}

So, every concrete graphic figure class encapsulates instance of concrete geometry class.
Is that approach is the right one, or should I prefer more generical one? - unfortunately, downcasting would be used in this case:

public class GraphicPrimitive {
   protected GeometryPrimitive figure; 
   ....
}

public class Rectangle: GraohicPrimitive {

        public Rectangle(Color c, TwoDPoint leftHighPoint, TwoDPoint rightLowPoint): 
            base(new RectangleGeometric(leftHighPoint.Point2D, rightLowPoint.Point2D), c) {   }

        #region Geometric Properties

        public TwoDPoint LeftTopCorner {
            get { return new TwoDPoint(base.color, (base.figure as RectangleGeometric).LeftTopCorner); }
        }

        public TwoDPoint RightBottomCorner {
            get { return new TwoDPoint(base.color, (base.figure as RectangleGeometric).RightBottomCorner); }
        }
+2  A: 

You don't give many details, so I can't really give my opinion about whether it is adequate to have the two hierarchies. We all know the costs of this parallelism, but only you can evaluate the advantages ...


I don't know your programming language. But a common solution to avoid the downcasting you mention, is to have a base class using parameterized types (called generics or templating). This works in Java, C++ and many modern languages.

The idea is that the precise type of the field base.figure is determined by each subclass. What you know in the base class is that it is a subtype T of GeometryPrimitive. In each subclass, you defined the precise type RectangleGeometric that replaces the T, so within this subclass the type is precise.

Java example:

    public GraphicPrimitive<T extends GeometryPrimitive> {
      protected T figure;
    }

    public Rectangle extends GraohicPrimitive<RectangleGeometric> {
      // It appears in this class that the field was defined as 
      //protected RectangleGeometric figure;

      public TwoDPoint getLeftTopCorner {
        return new TwoDPoint(base.color, figure.LeftTopCorner); 
      }
    }
KLE
+1 I would go with this one, it clearly expresses at compile time that you want the user of this class to define which specific class it will use.
eglasius
+4  A: 

While your question lacks some of the larger context about your application that would help with giving a specific answer, I'll try by giving you some ideas of how I would implement this using your code for inspiration.

I would start by inverting the relationship GeometryPrimitive and GraphicPrimitive. I see the the GeometryPrimitive hierarchy as the domain objects that make up your abstract scene graph and the GraphicPrimitive hierarchy as low level view components that translate a GeometryPrimitive into a set of pixels appropriate for drawing onto some kind of graphics context. The GeometryPrimitive subclasses hold all the state information necessary to describe themselves but no logic for translating that description into pixels. The GraphicPrimitive subclasses have all the pixel pushing logic, but no internal state. In effect, the GraphicPrimitive hierarchy represents a hierarchy of Command Objects.

In the GeometryPrimitive base class, include an abstract method called GetGraphicPrimitive(). In the GraphicPrimitive base class include an abstract method called Draw(Graphics g).

Within each GeometryPrimitive, include the appropriate GraphicPrimitive for drawing the object and an accessor method for accessing it. To draw the entire scene, walk your structure of GeometryPrimitive objects, asking each one for its GraphicPrimitive and then invoking the Draw() method.

abstract class GeometryPrimitive
{
    public abstract GraphicsPrimitive GetGraphicsPrimitive();
}

abstract class GraphicsPrimitive
{
    public abstract void Draw(Graphics g);
}

class RectangleGeometryPrimitive : GeometryPrimitive
{
    public Point TopLeft {get; set;}
    public Point BottomRight {get; set;}

    private RectangleGraphicPrimitive gp;

    public RectanglePrimitive(Point topLeft, Point bottomRight);
    {
        this.TopLeft = topLeft;
        this.BottomRight = bottomRight;
        this.gp = new RectangleGraphicsPrimitive(this);
    }

    public GraphicsPrimitive GetGraphicsPrimitive()
    {
        return gp;
    }
}

class RectangleGraphicsPrimitive : GraphicsPrimitive
{
    private RectangleGeometryPrimitive p;

    public RectangleGraphicsPrimitive(RectangleGeometryPrimitive p)
    {
        this.p = p;
    }

    public void Draw(Graphics g)
    {
        g.DrawRectangle(p.TopLeft, p.BottomRight);
    }
}

class CircleGeometryPrimitive : GeometryPrimitive
{
    public Point Center {get; set;}
    public int Radius {get; set;}

    private CircleGraphicPrimitive gp;

    public RectanglePrimitive(Point center, int radius);
    {
        this.Center = center;
        this.Radius = radius;
        this.gp = new CircleGraphicsPrimitive(this);
    }

    public GraphicsPrimitive GetGraphicsPrimitive()
    {
        return gp;
    }
}

class CircleGraphicsPrimitive : GraphicsPrimitive
{
    private CircleGeometryPrimitive p;

    public CircleGraphicsPrimitive(CircleGeometryPrimitive p)
    {
        this.p = p;
    }

    public void Draw(Graphics g)
    {
        g.DrawCircle(p.Center, p.Radius);
    }
}

As you can see above, no downcasting is required to draw GeometryPrimitives to the screen. With proper use of inheritance, you can also share GraphicsPrimitive objects between different GeometryPrimitives. For example, SquareGeometryPrimitive and RectangleGeometryPrimitive can both use RectangleGraphicsPrimitive if SquareGeometryPrimitive derives from RectangleGeometryPrimitive.

Ryan Michela
I followed your way, thanks
chester89
I'm glad it worked out for you :)
Ryan Michela