从单个对象继承的不同类型的集合与超级对象的单个集合

从单个对象继承的不同类型的集合与超级对象的单个集合

问题描述:

想象下面的情景。从单个对象继承的不同类型的集合与超级对象的单个集合

class Shape 
{ 
    // methods of Class Shape 
} 

class Circle extends Shape 
{ 
    // methods of Class Shape 
    // methods of Class Circle 
} 

class Square extends Shape 
{ 
    // methods of Class Shape 
    // methods of Class Square 
} 

class Canvas // This class should a bucket of objects that belong to Square and Circle 
{ 
    // Declaring a single list of supertype 
    List<Shape> heterogeneousCollection; 

    // Declaring two separate lists 
    List<Cirlce> homogeneousCollection; 
    List<Square> homogeneousCollection; 
} 

相应的关系模式是如下

  • 表Shape包含主信息
  • 表圈&广场通过的外键引用的方式富集在形状数据(圆& - 正方形是不相交的设置不同的列)
  • 表画布是一种数据聚合
  • 画布连接圈(1..1基数)
  • 帆布加入广场(1..1基数)

哪个是更好的方法“声明超单列表(均质集合体)”或“声明两个单独的列表(两种不同的同质收藏品)“

我在做出决定时正在考虑以下几点。

  1. 当从数据库中读取数据时,我们如何填充对象Canvas?

考虑避免N + 1点的问题,从一个单一的查询像

SELECT * FROM 
Canvas INNER JOIN Circle ON .. 
Canvas INNER JOIN Square ON .. 
Circle INNER JOIN Shape ON .. 
Square INNER JOIN Shape ON .. 

阅读现在,Canvas的每一个记录,我们结束了(B + C)行的方式。但是,有了几个ORM工具,可以将Circle和Square中不同的数据组分成两个单独的列表。 (我在考虑iBatis)

  1. 我们如何处理这些对象的几个函数?
    想象我们打算处理UI功能以在对象Canvas中显示数据的情况。除了Circle和Square之间的共同功能,它们每个都可以具有不同的功能。例如Square可以有getLengthOfSide(),而Circle可以有getRadius()。如果我们使用异构列表,我们最终可能会在每个需要访问这些函数的地方使用一个cast操作符。

    Class Canvas 
    { 
        void drawAll() 
        { 
         for (Shape s : heterogeneousCollection) 
         { 
          if (s instanceof Circle) 
          { 
           s.draw(); // common function present in Shape also 
           s.xyz(); // specific to Circle 
          } 
          if (s instanceof Square) 
          { 
           s.draw(); // common function present in Shape also 
           s.abc(); // specific to Square 
          } 
         } 
        } 
    } 
    

两种同质名单情况下,我们可能有单独为每个列表循环两种不同的。但是,如果我们需要添加一种新的形状(比如三角形),它会影响Canvas,我觉得这是设计缺陷的结果,Java可能会配备来处理这个问题。请在此点亮一些。任何对书籍/链接的引用都会有很大的帮助。只是想告诉大家,这不是一个学校任务,我正在认真研究各种解决方案。请原谅我一个长期的问题。

PS:另一个解决方案List<? extends Shape>已被排除,因为我们无法在此集合中插入任何对象。

+0

更正:在for循环中,铸造之前调用UI功能。 Circle c =(Cirle)s; c.draw(); c.abc(); ... – Gopal 2010-10-15 16:03:38

+0

我不明白你的“PS”的评论。 – 2010-10-15 17:33:45

假设有一些地方你合法需要处理的处理圆和方独立,我会通过应用Visitor Pattern

这样做的好处应对非均质集合体,如果你加入然后当你向访问者添加一个方法 T visitTriangle(Triangle triangle);时,你的代码将不会编译,直到你更新每一个访问者,避免令人讨厌的运行时间意外。

但是......如果你真的只是谈论一个单独的处理Circle和Square的实例,那么在这里应用Visitor是过量的,我只是考虑在Shape中添加一个抽象方法doSomeSpecificUiThing()

这将是这个样子:

class ShapeVisitor<T> 
{ 
    T visitCircle(Circle circle); 
    T visitSquare(Square square); 
} 

class Shape 
{ 
    abstract <T> T accept(ShapeVisitor<T> visitor); 

    // methods of Class Shape 
} 

class Circle extends Shape 
{ 
    <T> T accept(ShapeVisitor<T> visitor) { 
     return visitor.visitCircle(this); 
    } 

    // methods of Class Circle 
} 

class Square extends Shape 
{ 
    <T> T accept(ShapeVisitor<T> visitor) { 
     return visitor.visitSquare(this); 
    } 

    // methods of Class Square 
} 

Class Canvas 
{ 
    void drawAll() 
    { 
     for (Shape s : heterogeneousCollection) 
     { 
      s.draw(); 
      s.accept(new ShapeVisitor<Void>() { 
       @Override Void visitCircle(Circle circle) { 
        circle.xyz(); 
        return null; 
       } 

       @Override Void visitSquare(Square square) { 
        square.abc(); 
        return null; 
       } 
      } 
     } 
    } 
} 
+0

非常感谢您的回复。我会尝试阅读更多关于访客模式的信息。 – Gopal 2010-10-16 14:27:33

+0

好吧,现在我得到了很好的答案,我想分享一下,iBatis确实支持继承映射。这可以使用resultMap的“鉴别器”和“子类型”属性。仔细阅读文档可以提供更多帮助。 – Gopal 2010-10-16 14:45:57

当我们拥有一般信息而不是具体事实时,很难给出解决方案。例如,xyz()和abc()方法。他们是什么,为什么?在你的例子中,它们似乎有相似的用法,所以我会考虑在Shape中定义一个名为doS​​omethingSpecific()的抽象方法,以便它将由所有Shape子类实现。 现在你的代码是:

void drawAll() 
     { 
     for (Shape s : heterogeneousCollection) 
      { 
      s.draw();    // common function present in Shape also 
      s.doSomethingSpecific(); // specific to each implementation 
      } 
     } 

我喜欢收藏的异构可能的情况下。我非常不喜欢instanceof,因为您指出 - 当我们将Triangle添加到组合中时会发生什么。

+0

非常感谢您的回复。 – Gopal 2010-10-16 14:26:54