2010-06-02 5 views
3

J'écris une structure de données spatiales et j'ai un doute sur la meilleure implémentation de NODE. Selon ma conception, j'ai une entité de nœud abstrait et trois classes qui en héritent: EMPTYNODE, FULLNODE, INTERNALNODE.Implémentation de l'héritage conceptuel

Le premier n'a pas de données particulières.

La seconde a 1 référence à un élément générique.

Le troisième a 2 références à d'autres nœuds.

J'ai trouvé plusieurs façons de mettre en œuvre cette situation (que j'ai déjà codé) mais je ne peux pas décider ce qui est le meilleur.

La première solution que j'ai trouvé est d'utiliser un seul nœud de classe qui effectue potentiellement toutes les opérations de cette façon:

private static class Node { 
    private Elem elem = null; 
    private Node left = null, right = null; 
    public Elem getElem() { 
     assert isFull(); 
     return elem; 
    } 

    public boolean isEmpty() { 
     return elem == null && left == null; 
    } 

    public boolean isFull() { 
     return elem != null; 
    } 


    public boolean isInternal() { 
     return elem == null && left != null; 
    } 
} 

La deuxième solution consiste à écrire une division explicite des classes où chaque des offres de classe seulement ses méthodes. De toute évidence, nous sommes obligés d'effectuer plusieurs lancers sur les objets du nœud.

private static abstract class Node { 

    public abstract boolean isEmpty(); 

    public abstract boolean isFull(); 

    public abstract boolean isInternal(); 

} 


private static class FullNode extends Node{ 

    private ITriangle elem; 

    @Override 
    public boolean isEmpty() { 
     return false; 
    } 

    @Override 
    public final boolean isFull() { 
     return true; 
    } 

    @Override 
    public final boolean isInternal() { 
     return false; 
    } 

    public Elem getElem() { 
     return elem; 
    } 
} 

La troisième solution est d'utiliser l'héritage permettant à chaque classe d'offrir toutes les méthodes, mais le type d'objet doit par chèque par « isEmpty() » et des méthodes similaires. En cas de mauvais appel, nous allons lancer une exception.

private static abstract class Node { 

    public abstract boolean isEmpty(); 

    public abstract boolean isFull(); 

    public abstract boolean isInternal(); 

    public abstract Elem getElem(); 

} 


private static class Empty extends Node{ 

    @Override 
    public boolean isEmpty() { 
     return true; 
    } 

    @Override 
    public final boolean isFull() { 
     return false; 
    } 

    @Override 
    public final boolean isInternal() { 
     return false; 
    } 

    @Override 
    public Elem getElem() { 
     throw new AssertionError(); 
    } 
} 

Que pensez-vous de ces trois solutions?

Lequel utiliseriez-vous?

Toutes les idées seront appréciées.

Répondre

1

La réponse dépend de la façon dont les nœuds sont utilisés. Une fois qu'un noeud est créé, devra-t-il changer de vide à interne ou à plein, ou est-il immuable?

dans les options si un nœud est immuable alors je vais pour l'option 3 si vous vous attendez à l'état interne de changer alors je partirais avec l'option 1.

Si vous voulez un nœud mutable où le comportement changerait je regarderais en utilisant une classe de noeud pour maintenir les données avec une énumération pour maintenir les états, le noeud déléguerait alors à l'énumération appropriée pour son exécution. Par exemple:

public class Node { 

    private enum NodeState { 
     /* each state overrides specific methods to implement custom behaviour */ 
     FULL { public boolean isFull() { return true; } }, 
     INTERNAL { public boolean isInternal() { return true; } }, 
     EMPTY { public boolean isEmpty() { return true; } }; 

     /* the default behaviour */ 
     public boolean isFull() { return false; } 
     public boolean isEmpty() { return false; } 
     public boolean isInternal() { return false; } 
    } 

    private NodeState state = NodeState.EMPTY; 
    private Elem  elem = null; 
    private Node  left = null, right = null; 

    public Elem getElem() { 
     assert isFull(); 
     return elem; 
    } 

    /* TODO: constructors/mutators implement state changes go here */ 

    public boolean isEmpty() { 
     return state.isEmpty(); 
    } 

    public boolean isFull() { 
     return state.isFull(); 
    } 

    public boolean isInternal() { 
     return state.isInternal(); 
    } 
} 

class Elem { 
    /* implementation of this class */ 
} 
+0

Dans mon cas, ils doivent changer et en fait l'option 1 est la plus efficace (à la fois dans le temps et l'espace) parmi les trois. Merci pour votre aide. – TheSENDER

+0

Je pense à ce problème d'un point de vue théorique. Qu'en est-il de l'utilisation d'un modèle d'état? – TheSENDER

+0

Oui, c'était ce que j'essayais d'exprimer dans mon commentaire sur les nœuds mutables. J'ai ajouté un exemple d'utilisation d'enums pour implémenter le modèle d'état. Le bit qui mute les valeurs et change l'état est manquant car je ne suis pas sûr des changements que vous avez l'intention de permettre – BenM