【发布时间】:2010-05-25 22:19:05
【问题描述】:
我正在为不同类型的树编写一组集合类。我将其作为一个学习练习,我也希望它成为有用的东西。我真的很想以正确的方式做到这一点,所以我一直在阅读Effective Java,并且我也一直在研究 Joshua Bloch 通过查看源代码实现集合类的方式。我似乎对正在做的事情有一个大致的了解,但我还有一些事情要解决。
我有一个Node<T> 接口和一个实现Node 接口的AbstractNode<T> 类。然后我创建了一个扩展AbstractNode<T> 和实现Node<T>。这部分很容易。
接下来,我创建了一个Tree<T> 接口和一个实现Tree<T> 接口的AbstractTree<T> 类。之后,我开始编写一个扩展AbstractTree<T> 并实现Tree<T> 的GenericTree<T> 类。这就是我开始遇到问题的地方。
就设计而言,GenericTree<T> 只能包含 GenericTreeNode<T> 类型的节点。这包括根。在我的Tree<T> 界面中,我有:
public interface Tree<T> {
void setRoot(Node<T> root);
Node<T> getRoot();
List<Node<T>> postOrder();
... rest omitted ...
}
而且,AbstractTree<T> 实现了这个接口:
public abstract class AbstractTree<T> implements Tree<T> {
protected Node<T> root;
protected AbstractTree() {
}
protected AbstractTree(Node<T> root) {
this.root = root;
}
public void setRoot(Node<T> root) {
this.root = root;
}
public Node<T> getRoot() {
return this.root;
}
... rest omitted ...
}
在GenericTree<T>,我可以拥有:
public GenericTree(Node<T> root) {
super(root);
}
但这意味着您可以使用Node<T> 的任何子类型创建通用树。您还可以将树的根设置为Node<T> 的任何子类型。我希望能够将节点的类型限制为它可以表示的树的类型。为了解决这个问题,我可以这样做:
public GenericTree(GenericNode<T> root) {
super(root);
}
但是,setRoot 仍然接受 Node<T> 类型的参数。这意味着用户仍然可以使用错误类型的根节点创建树。如何强制执行此约束?我能想到的唯一方法是:
- 执行
instanceof将检查限制在运行时。我不是这个的超级粉丝。 - 从接口中删除
setRoot并让基类实现此方法。这意味着它不是合同的一部分,任何想要制作新型树的人都需要记住实现此方法。
有没有更好的办法?
我的第二个问题涉及postOrder 的返回类型,即List<Node<T>>。这意味着如果用户对GenericTree<T> 对象进行操作并调用postOrder,他或她会收到一个由Node<T> 对象组成的列表。这意味着在迭代(使用 foreach 构造)时,如果他们想使用仅在该类中定义的方法,他们将执行显式转换为 GenericNode<T>。我不喜欢把这个负担放在用户身上。在这种情况下我有什么选择?我只能考虑从接口中删除该方法并让子类实现此方法,以确保它返回Node<T> 的适当子类型列表。但是,这再次将其从合同中删除,任何想要创建新型树的人都必须记住实现此方法。有没有更好的办法?
【问题讨论】:
-
你确定你需要
setRoot方法吗?我记得 Effective Java 的一个建议是favor immutability。 -
@Alexander 这是真的。这实际上是我一直在考虑的另一个想法——
setRoot是否真的有必要。我开始倾向于它没有是必要的。第二个问题呢?
标签: java oop inheritance interface implementation