在阶级等级中频繁的低调总是邪恶吗?

时间:2009-09-28 08:14:57

标签: oop

根据我所学到的,如果经常在类层次结构中使用向下转换,那就不好了。 我同意这一点,但是如果有的话,这个规则的例外情况是什么? 这就是我的图形编辑器设计显得很薄的地方:我有两个层次结构,其中几何图形层次结构与图形基元分离。像这样:

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

因此,每个具体的图形图类都封装了具体几何类的实例。
这种方法是正确的,还是我更喜欢更通用的? - 不幸的是,向下转换将是在这种情况下使用:

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 个答案:

答案 0 :(得分:4)

虽然你的问题缺乏关于你的应用程序的一些更大的背景,这将有助于给出一个特定的答案,但我会尝试给你一些关于如何使用你的代码实现灵感的想法。

我将从反转GeometryPrimitive和GraphicPrimitive的关系开始。我将GeometryPrimitive层次结构视为构成抽象场景图的域对象,将GraphicPrimitive层次结构视为低级视图组件,将GeometryPrimitive转换为适合绘制到某种图形上下文的一组像素。 GeometryPrimitive子类包含描述自身所需的所有状态信息,但没有将该描述转换为像素的逻辑。 GraphicPrimitive子类具有所有像素推送逻辑,但没有内部状态。实际上,GraphicPrimitive层次结构表示Command Objects的层次结构。

在GeometryPrimitive基类中,包含一个名为GetGraphicPrimitive()的抽象方法。在GraphicPrimitive基类中包含一个名为Draw(Graphics g)的抽象方法。

在每个GeometryPrimitive中,包括用于绘制对象的相应GraphicPrimitive和用于访问它的访问器方法。要绘制整个场景,请遍历GeometryPrimitive对象的结构,询问每个对象的GraphicPrimitive,然后调用Draw()方法。

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);
    }
}

正如您在上面所看到的,不需要向下转换就可以将GeometryPrimitives绘制到屏幕上。通过正确使用继承,您还可以在不同的GeometryPrimitive之间共享GraphicsPrimitive对象。例如,如果SquareGeometryPrimitive派生自RectangleGeometryPrimitive,则SquareGeometryPrimitive和RectangleGeometryPrimitive都可以​​使用RectangleGraphicsPrimitive。

答案 1 :(得分:2)

你没有提供很多细节,所以我无法真正表达我对这两个等级是否足够的看法。我们都知道这种并行性的成本,但只有你能评估它们的优势......


我不懂你的编程语言。但是,避免你提到的向下转换的一个常见解决方案是使用参数化类型(称为泛型或模板)来建立基类。这适用于Java,C ++和许多现代语言。

我们的想法是,字段base.figure的精确类型由每个子类决定。您在基类中知道的是它是GeometryPrimitive的子类型T.在每个子类中,您定义了替换T的精确类型RectangleGeometric,因此在此子类中,类型是精确的。

Java示例:

    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); 
      }
    }