从单个对象继承的不同类型的集合与超级对象的单个集合
想象下面的情景。从单个对象继承的不同类型的集合与超级对象的单个集合
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基数)
哪个是更好的方法“声明超单列表(均质集合体)”或“声明两个单独的列表(两种不同的同质收藏品)“
我在做出决定时正在考虑以下几点。
- 当从数据库中读取数据时,我们如何填充对象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)
-
我们如何处理这些对象的几个函数?
想象我们打算处理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>
已被排除,因为我们无法在此集合中插入任何对象。
假设有一些地方你合法需要处理的处理圆和方独立,我会通过应用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;
}
}
}
}
}
当我们拥有一般信息而不是具体事实时,很难给出解决方案。例如,xyz()和abc()方法。他们是什么,为什么?在你的例子中,它们似乎有相似的用法,所以我会考虑在Shape中定义一个名为doSomethingSpecific()的抽象方法,以便它将由所有Shape子类实现。 现在你的代码是:
void drawAll()
{
for (Shape s : heterogeneousCollection)
{
s.draw(); // common function present in Shape also
s.doSomethingSpecific(); // specific to each implementation
}
}
我喜欢收藏的异构可能的情况下。我非常不喜欢instanceof
,因为您指出 - 当我们将Triangle
添加到组合中时会发生什么。
非常感谢您的回复。 – Gopal 2010-10-16 14:26:54
更正:在for循环中,铸造之前调用UI功能。 Circle c =(Cirle)s; c.draw(); c.abc(); ... – Gopal 2010-10-15 16:03:38
我不明白你的“PS”的评论。 – 2010-10-15 17:33:45