Devrais-je utiliser un clone lors de l'ajout d'un nouvel élément? Quand faut-il utiliser le clone?

StackOverflow https://stackoverflow.com/questions/63748

  •  09-06-2019
  •  | 
  •  

Question

Je souhaite implémenter en Java une classe pour la gestion des structures de données graphiques. J'ai une classe Node et une classe Edge. La classe Graph gère deux listes: une liste de nœuds et une liste d'arêtes. Chaque nœud doit avoir un nom unique. Comment puis-je me protéger contre une situation comme celle-ci:

Graph g = new Graph();

Node n1 = new Node("#1");
Node n2 = new Node("#2");

Edge e1 = new Edge("e#1", "#1", "#2");

// Each node is added like a reference
g.addNode(n1);
g.addNode(n2);
g.addEdge(e1);

// This will break the internal integrity of the graph
n1.setName("#3");   
g.getNode("#2").setName("#4"); 

Je pense que je devrais cloner les noeuds et les bords lors de leur ajout au graphique et renvoyer une classe NodeEnvelope qui préservera l'intégrité structurelle du graphique. Est-ce la bonne façon de procéder ou le design est-il cassé depuis le début?

Était-ce utile?

La solution

Je travaille souvent avec des structures de graphes en Java, et mon conseil serait de rendre n'importe quel membre de la classe Node and Edge dont le graphe dépend pour maintenir sa structure finale, sans aucun paramètre. En fait, si vous le pouviez, je rendrais Node and Edge complètement immuable, ce qui a de nombreux avantages .

Ainsi, par exemple:

public final class Node {

    private final String name;

    public Node(String name) {
           this.name = name;
    }

    public String getName() { return name; }
    // note: no setter for name
}

Vous feriez ensuite votre vérification d'unicité dans l'objet graphique:

public class Graph {
    Set<Node> nodes = new HashSet<Node>();
    public void addNode(Node n) {
        // note: this assumes you've properly overridden 
        // equals and hashCode in Node to make Nodes with the 
        // same name .equal() and hash to the same value.
        if(nodes.contains(n)) {
            throw new IllegalArgumentException("Already in graph: " + node);
        }
        nodes.add(n);
    }
}

Si vous devez modifier le nom d'un nœud, supprimez l'ancien nœud et ajoutez-en un nouveau. Cela peut sembler être un surcroît de travail, mais vous épargnez beaucoup d’efforts.

Vraiment, créer votre propre structure graphique à partir de rien n'est probablement pas nécessaire. Ce problème n'est que le premier parmi d'autres que vous risquez de rencontrer si vous créez votre propre structure.

Je vous recommanderais de rechercher une bonne bibliothèque de graphes Java open source et de l'utiliser à la place. Selon ce que vous faites, il existe quelques options. J'ai déjà utilisé JUNG , et le recommanderais comme bon point de départ.

Autres conseils

Je ne comprends pas vraiment pourquoi vous ajoutez l'indirection supplémentaire des noms de chaîne pour les nœuds. Ne serait-il pas plus logique que la signature de votre constructeur Edge soit quelque chose comme Edge public (String, Node, Node) au lieu de Edge public (String, String, String) ?

Je ne sais pas où un clone vous aiderait ici.

ETA: si le nom du nœud a été modifié après la création du nœud, lancez une IllegalOperationException si le client tente d'appeler setName () sur un nœud portant un nom existant.

À mon avis, vous ne devez jamais cloner l'élément à moins d'indiquer explicitement que votre structure de données le fait.

La plupart des fonctionnalités souhaitées nécessitent que l'objet lui-même soit transmis à la structure de données par référence.

Si vous souhaitez sécuriser la classe Node , faites-en une classe interne du graphique.

L'utilisation de NodeEnvelopes ou d'usines de bord / nœuds me ressemble à une sur-conception.

Voulez-vous vraiment exposer une méthode setName () sur Node? Rien dans votre exemple n'indique que vous en avez besoin. Si vous rendez les classes Node et Edge immuables, la plupart des scénarios de violation de l'intégrité que vous envisagez deviennent impossibles. (Si vous avez besoin qu'ils soient mutables mais seulement jusqu'à ce qu'ils soient ajoutés à un graphique, vous pouvez le faire en disposant un indicateur isInGraph sur vos classes Node / Edge défini sur true par Graph.Add {Node, Edge} et demandez à vos mutateurs de lancer une exception s'ils sont appelés après que cet indicateur a été défini.)

Je suis d'accord avec jhkiley pour dire que passer des objets Node au constructeur Edge (au lieu de Strings) semble être une bonne idée.

Si vous souhaitez une approche plus intrusive, vous pouvez insérer un pointeur de la classe de nœud sur le graphe dans lequel il réside et mettre à jour le graphe si des propriétés critiques (le nom, par exemple) du nœud changent. Mais je ne le ferais que si vous êtes certain de pouvoir modifier les noms des nœuds existants tout en préservant les relations Edge, ce qui semble peu probable.

Object.clone () a quelques problèmes majeurs et son utilisation est découragée dans la plupart des cas. Reportez-vous à la rubrique 11 de Java efficace . de Joshua Bloch pour une réponse complète. Je pense que vous pouvez utiliser en toute sécurité Object.clone () sur des tableaux de type primitif, mais à part cela, vous devez faire preuve de discernement pour utiliser correctement et remplacer le clone. Il est probablement préférable de définir un constructeur de copie ou une méthode de fabrique statique qui clone explicitement l’objet en fonction de votre sémantique.

En plus des commentaires de @ jhkiley.blogspot.com, vous pouvez créer une usine pour les bords et les nœuds qui refuse de créer des objets avec un nom déjà utilisé.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top