¿Debo usar clonar al agregar un nuevo elemento?¿Cuándo se debe utilizar la clonación?

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

  •  09-06-2019
  •  | 
  •  

Pregunta

Quiero implementar en Java una clase para manejar estructuras de datos de gráficos.Tengo una clase Node y una clase Edge.La clase Graph mantiene dos listas:una lista de nodos y una lista de aristas.Cada nodo debe tener un nombre único.¿Cómo me protejo contra una situación como esta?

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"); 

Creo que debería clonar los nodos y los bordes al agregarlos al gráfico y devolver una clase NodeEnvelope que mantendrá la integridad estructural del gráfico.¿Es esta la forma correcta de hacerlo o el diseño está roto desde el principio?

¿Fue útil?

Solución

Trabajo mucho con estructuras de gráficos en Java, y mi consejo sería hacer que cualquier dato sea miembro de la clase Node y Edge de la que depende Graph para mantener su estructura definitiva, sin configuradores.De hecho, si puedes, haría que Node y Edge fueran completamente inmutables, lo cual tiene muchos beneficios.

Así por ejemplo:

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
}

Luego harías tu verificación de unicidad en el objeto Graph:

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 necesita modificar el nombre de un nodo, elimine el nodo antiguo y agregue uno nuevo.Esto puede parecer un trabajo extra, pero ahorra mucho esfuerzo al mantener todo en orden.

En realidad, sin embargo, crear su propia estructura Graph desde cero probablemente sea innecesario; este problema es solo el primero de muchos con los que probablemente se encontrará si construye el suyo propio.

Recomendaría encontrar una buena biblioteca de gráficos Java de código abierto y usarla en su lugar.Dependiendo de lo que esté haciendo, existen algunas opciones.He usado JUNG en el pasado y lo recomendaría como un buen punto de partida.

Otros consejos

No me queda claro por qué está agregando la dirección indirecta adicional de los nombres de cadenas para los nodos.¿No tendría más sentido que la firma del constructor de Edge fuera algo como public Edge(String, Node, Node) en lugar de public Edge (String, String, String)?

No sé dónde te ayudaría clonar aquí.

Hora estimada de llegada:Si el peligro proviene de cambiar el nombre del nodo después de crearlo, lance un IllegalOperationException si el cliente intenta llamar a setName() en un nodo con un nombre existente.

En mi opinión, nunca deberías clonar el elemento a menos que indiques explícitamente que tu estructura de datos lo hace.

La funcionalidad deseada de la mayoría de las cosas necesita que el objeto real se pase a la estructura de datos por referencia.

Si quieres hacer el Node clase más segura, conviértala en una clase interna del gráfico.

Usar NodeEnvelopes o Edge/Node Factories me parece un diseño excesivo.

¿Realmente desea exponer un método setName() en Node?No hay nada en su ejemplo que sugiera que lo necesite.Si hace que sus clases Node y Edge sean inmutables, la mayoría de los escenarios de violación de integridad que está imaginando se vuelven imposibles.(Si necesita que sean mutables, pero solo hasta que se agreguen a un gráfico, puede aplicar esto teniendo un indicador isInGraph en sus clases de Nodo/Edge que Graph.Add{Node, Edge} establezca en verdadero y haga que sus mutadores lancen una excepción si se les llama después de establecer este indicador).

Estoy de acuerdo con jhkiley en que pasar objetos Node al constructor Edge (en lugar de Strings) parece una buena idea.

Si desea un enfoque más intrusivo, puede tener un puntero de la clase Nodo al Gráfico en el que reside y actualizar el Gráfico si alguna propiedad crítica (por ejemplo, el nombre) del Nodo cambia alguna vez.Pero no haría eso a menos que esté seguro de que necesita poder cambiar los nombres de los nodos existentes y al mismo tiempo preservar las relaciones de borde, lo cual parece poco probable.

Object.clone() tiene algunos problemas importantes y se desaconseja su uso en la mayoría de los casos.Consulte el punto 11, de "Java efectivo" de Joshua Bloch para obtener una respuesta completa.Creo que puedes usar Object.clone() de forma segura en matrices de tipo primitivo, pero aparte de eso, debes ser prudente a la hora de usar y anular clonar correctamente.Probablemente sea mejor definir un constructor de copia o un método de fábrica estático que clone explícitamente el objeto de acuerdo con su semántica.

Además de los comentarios de @jhkiley.blogspot.com, puedes crear una fábrica para Bordes y Nodos que se niegue a crear objetos con un nombre que ya se usó.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top