J'écris un ensemble de classes de collection pour différents types d'arbres. Je fais cela en tant qu'exercice d'apprentissage et j'espère aussi que cela s'avérera utile. Je veux vraiment le faire de la bonne façon et donc j'ai lu Effective Java et j'ai aussi regardé la façon dont Joshua Bloch a implémenté les classes de collection en regardant la source. Je semble avoir une bonne idée de ce qui se fait, mais j'ai encore quelques choses à régler.Question de mise en œuvre impliquant l'implémentation d'une interface
J'ai une interface Node<T>
et une classe AbstractNode<T>
qui implémente l'interface Node
. J'ai ensuite créé un GenericNode<T>
(un nœud pouvant avoir 0 à n enfants, et cela fait partie d'une classe n -ary tree) qui étend AbstractNode<T>
et implémente Node<T>
. Cette partie était facile.
Ensuite, j'ai créé une interface Tree<T>
et une classe AbstractTree<T>
qui implémente l'interface Tree<T>
. Après cela, j'ai commencé à écrire une classe GenericTree<T>
qui s'étend AbstractTree<T>
et implémente Tree<T>
. C'est là que j'ai commencé à avoir des problèmes. En ce qui concerne la conception, un GenericTree<T>
ne peut être composé que de nœuds de type GenericTreeNode<T>
. Cela inclut la racine. Dans mon interface Tree<T>
je:
public interface Tree<T> {
void setRoot(Node<T> root);
Node<T> getRoot();
List<Node<T>> postOrder();
... rest omitted ...
}
Et, AbstractTree<T>
implémente cette interface:
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 ...
}
En GenericTree<T>
, je peux avoir:
public GenericTree(Node<T> root) {
super(root);
}
Mais ce que cela signifie est que vous pouvez créer un arbre générique en utilisant un sous-type de Node<T>
. Vous pouvez également définir la racine d'un arbre à n'importe quel sous-type de Node<T>
. Je veux pouvoir limiter le type du noeud au type de l'arbre qu'il peut représenter. Pour résoudre ce problème, je peux le faire:
public GenericTree(GenericNode<T> root) {
super(root);
}
Cependant, setRoot
accepte encore un paramètre de type Node<T>
. Ce qui signifie qu'un utilisateur peut toujours créer un arbre avec le mauvais type de nœud racine. Comment puis-je appliquer cette contrainte? La seule façon que je peux penser à faire est soit:
- Faire un qui limite la vérification à l'exécution. Je ne suis pas un grand fan de ça.
- Supprimez
setRoot
de l'interface et demandez à la classe de base d'implémenter cette méthode. Cela signifie que cela ne fait pas partie du contrat et que quiconque veut créer un nouveau type d'arbre doit se rappeler de mettre en œuvre cette méthode.
Y a-t-il un meilleur moyen?
La deuxième question que j'ai concerne le type de retour de postOrder
qui est List<Node<T>>
. Cela signifie que si un utilisateur fonctionne sur un objet GenericTree<T>
et appelle postOrder
, il reçoit une liste composée d'objets Node<T>
.Cela signifie que lors de l'itération (à l'aide d'une construction foreach), ils auraient effectué une conversion explicite en GenericNode<T>
s'ils voulaient utiliser des méthodes définies uniquement dans cette classe. Je n'aime pas avoir à imposer ce fardeau à l'utilisateur. Quelles sont mes options dans ce cas? Je ne peux penser à supprimer la méthode de l'interface et que la sous-classe implémente cette méthode en s'assurant qu'elle renvoie une liste de sous-type approprié de Node<T>
. Cependant, ceci le supprime encore du contrat et c'est n'importe qui qui veut créer un nouveau type d'arbre doit se rappeler d'implémenter cette méthode. Y a-t-il un meilleur moyen?
Etes-vous sûr que vous avez besoin 'méthode setRoot' du tout? Je me souviens qu'un conseil d'Effective Java est de "favoriser l'immuabilité". –
@Alexander C'est vrai. C'est en fait une autre idée à laquelle je pensais - si 'setRoot' est vraiment nécessaire. Je commence à m'y pencher * pas * étant nécessaire. Qu'en est-il de la deuxième question? –