2011-01-19 70 views
1

我目前正在设计模拟项目的类层次结构。这将是一个Element实例树的离散事件模拟。为了简洁起见(因为我只对这个问题的泛型部分感兴趣),所以我只会在这里展示类/接口的草图,所以语法不会很完美。有东西下手,一个元素可能是这样的:用泛型限制容器

public abstract class Element { 
    private Set<Element> children = new HashSet<Element>(); 
    public Element getContainer(); 
    public Collection<Element> getChildren() { 
     return Collections.unmodifiableSet(children); 
    } 
    public Position getPosition(); 
    protected void add(Element e) { children.add(e); } 
    protected void remove(Element e) {children.remove(e); } 
} 

Position的定义并不重要,什么其他的方法都应该做的是不言自明的希望。为了使事情变得有趣婉转我们抛出另一个接口入池:

public abstract class Solid extends Element { 
    public Size getSize(); 
} 

再次,Size的确切含义并不重要,一个Solid只是应该是一个物理对象,在模拟世界充满空间。现在我们已经有了坚实的对象,我们可能要堆在他们彼此的顶部,所以这里有一个Stack

public abstract class Stack extends Solid { 
    public void add(Solid s); // <- trouble ahead! 
} 

而这里的麻烦就来了。我真的只想要将Solid的实例添加到Stack。这是因为堆叠没有大小的物体很困难,所以Stack需要一些具有Size的东西。因此,我返工我Element允许表达这样的:

public abstract class Element<E extends Element<?>> { 
    private final Set<E> children = new HashSet<E>(); 
    public Element<?> getContainer(); 
    public Collection<E> getChildren(); 
    public Position getPosition(); 
    public void add(E e); 
    public void remove(E e); 
} 

我在这里的目的是要表达一个Element可以有上(子)类型的Element它可能包含的限制。到目前为止,这工作和所有。但是现在我觉得有必要对Element容器加以限制。这是什么样子?

public interface Element<E extends Element<?,?>, C extends Element<?, ?>> 

抑或是去更是这样的:

public interface Element<E extends Element<?,C>, C extends Element<E, ?>> 

我开始觉得有点模糊关于这一点,有更多的游戏:

  • 有有是“端口”(InputOutput),将元素从一个容器转移到另一个容器。我也必须在这些方面抛出一些泛型魔法。
  • 我的待办事项列表中有模型的GUI编辑器。所以我必须在运行时也提供这些检查。所以我猜想会出现类型文字和编辑器必须依赖的public boolean canAdd(Element<?, ?> e)之类的东西。因为这个方法非常重要,所以我希望它有Element类的final方法,所以没有醉酒的开发人员可能在凌晨4点出错。
  • 这将是很酷,如果Element的子类可以自己决定他们正在使用的类,因为从一些LinkedList或任何似乎是完美的适合。

因此,这里是我的问题:

  • 我是重新发明轮子呢?哪个库为我做?
  • 可以,我应该用泛型来解决这个问题吗? (如果答案是肯定的:对实现的一些提示非常欢迎)
  • 这可能有点过度设计?
  • 这个问题有更好的标题吗? :-)
  • (刚落,在这里您的意见)
+0

这里有一个想法给你。 java中泛型的目的是防止ClassCastException。句号。这是他们以理智的方式做的唯一事情。如果你试图用它来做其他事情,那就是疯狂。 – Affe 2011-01-19 21:07:26

+0

“这种方式对疯狂是谎言”,哈哈......在我了解类型删除之前,我尝试了各种各样的泛型技巧,但很快就了解到,大多数情况下,泛型的创造性使用注定要失败。如果它看起来真的很疯狂像元素,C扩展元素>你应该怀疑使用它们。概念化这些用途太难了。 – 2011-01-19 21:25:30

回答

1

我还是有点模糊,以你想要做什么,但如果你想用这一切的方式运行,以合乎逻辑的结论,我看到这样的事情:

/** 
@param P parent 
@param C child 
@param S self 
*/ 
interface Element<P extends Element<?, S, P>, 
     C extends Element<S, ?, C> , 
     S extends Element<P, C, S>> { 
    public P getParent(); 
    public Collection<C> getChildren(); 
} 

表达(我认为)所有你的约束:我的父母必须是能够让我当小孩的东西,而我的孩子必须是可以让我作为父母的东西。

实现将包括:

/** An agglomeration of stacks: no parent */ 
class Agglomeration extends Element<?, Stack, Agglomeration> {…} 

/** A stack of solids */ 
class Stack extends Element<Agglomeration, Solid, Stack> {…} 

/** A solid in a stack: no children */ 
class Solid extends Element<Stack, ?, Solid> {…} 

我没有一个Java IDE中打开,现在,虽然,所以我无法证实这实际上编译 - 我有一种感觉,编译器会抱怨关于这些问号,并尝试拖动到类似

class Agglomeration extends Element<? 
    extends Element<? 
     extends Element<? 
      extends... 

你可以解决这个问题,不过,如果你分开ParentChild成独立的接口(一个与getChildren()和一个与getParent()),所以Agglomeration只实现第一个和Solid只实现第二个,而Stack实现两者。

这可能比它的价值更麻烦,但运行它一段时间,看看它需要你。 :)

0

我立足于我对你的设计的理解这个答案。如果我的理解错了,请告诉我!

泛型的主要目的是确保类型安全,而不是对象关系(据我所知,无论如何)。看起来你可以通过确保类型安全来增强对象之间的关系,但最终的目的是确保类型安全(这样你就不会粘在不属于它的地方)。

在您的抽象类中,您提供了一种名为add的方法。这意味着任何扩展这个抽象类的东西都必须实现add。现在稍后,您对add施加限制,说您只能使用addSolid对象(它是Element的子类)。但是在这样做的时候,你违反了抽象类的合约,它说可以将Element(或其子类)添加到集合中。

所以有两种选择。你要么重新考虑你的设计(也许add不应该在抽象类中提供?)。另一种选择是在尝试add没有大小的对象时抛出运行时异常(可能为UnsupportedOperationException),并且还提供一种检查方法(如canAdd),以便开发人员知道是否可以添加特定对象。

1

只是一些随机的言论:

相反的Collection<E>你可能会或可能不希望Collection<? extends E>

我很确定,您想从Element<E extends Element<**E**>开始,因为您想捕获此类型(只需查看java.lang.Enum)。所以你需要的可能就像Element<E extends Element<E,C>, C extends Element<C, ?>> 对于你和编译器来说,它可能会变得太复杂(也许它会变得更好,但是前一段时间,我在尝试太难时遇到了一些非常奇怪的错误)。

也许方法Element.add和Stack.add实际上是不同的方法?将孩子添加到Stack意味着将它们堆叠在一起,将它们添加到任意Element可能意味着完全不同的东西?

如果Element的子类可以为他们自己决定使用哪个集合类,那是很酷的,因为从某个LinkedList或任何看起来很合适的东西。

在基类的ctor中调用Collection<C> makeCollection()等方法可以。然而,使用LinkedList在大多数情况下都是错误的(它的坏空间局部性和高空间开销使得它很慢,所以它的优点可能被忽略)。

所以我必须在运行时也提供这些检查。

因此,您需要使用Class<E>参数构造实例。

return Collections.unmodifiableSet(children);

这样您就可以返回儿童的实时视图。考虑使用com.google.common.collect.ImmutableSet.copyOf(儿童)代替。

0

望着这行:

public interface Element<E extends Element<?,?>, C extends Element<?, ?>> 

让我觉得是的,这肯定是过度设计。

当然,有很多方法不适用于这种情况,但对我来说合适的一种方法是使用Decorator模式而不是继承(不是在任何地方,只有在您觉得原始设计正在磨损的地方),也可能使用泛型也会更好地适应。