Должен ли я использовать clone при добавлении нового элемента?Когда следует использовать clone?

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

  •  09-06-2019
  •  | 
  •  

Вопрос

Я хочу реализовать в Java класс для обработки графических структур данных.У меня есть класс Node и класс Edge.Класс Graph поддерживает два списка:список узлов и список ребер.Каждый узел должен иметь уникальное имя.Как мне защититься от подобной ситуации:

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

Я считаю, что мне следует клонировать узлы и ребра при добавлении их в график и возвращать класс NodeEnvelope, который будет поддерживать структурную целостность графика.Правильно ли это делается , или дизайн нарушен с самого начала ?

Это было полезно?

Решение

Я много работаю со структурами графов в Java, и мой совет состоял бы в том, чтобы сделать любой элемент данных класса Node и Edge, от которого зависит график, для поддержания его структуры окончательной, без установщиков.На самом деле, если вы можете, я бы сделал узел и Ребро полностью неизменяемыми, которые имеют множество преимуществ.

Так, например:

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
}

Затем вы должны выполнить проверку своей уникальности в объекте 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);
    }
}

Если вам нужно изменить имя узла, удалите старый узел и добавьте новый.Это может показаться дополнительной работой, но это экономит массу усилий, поддерживая все в порядке.

На самом деле, однако, создавать свою собственную графическую структуру с нуля, вероятно, не нужно - эта проблема лишь первая из многих, с которыми вы, вероятно, столкнетесь, если создадите свою собственную.

Я бы порекомендовал найти хорошую библиотеку Java graph с открытым исходным кодом и использовать ее вместо этого.В зависимости от того, что вы делаете, существует несколько вариантов.Я использовал ЮНГ в прошлом и рекомендовал бы это как хорошую отправную точку.

Другие советы

Мне непонятно, почему вы добавляете дополнительную косвенность имен строк для узлов.Разве не было бы больше смысла, если бы подпись вашего конструктора Edge была чем-то вроде public Edge(String, Node, Node) вместо того, чтобы public Edge (String, String, String)?

Я не знаю, где clone мог бы вам здесь помочь.

ETA:Если опасность исходит из изменения имени узла после создания узла, выбросьте IllegalOperationException если клиент пытается вызвать setName() на узле с существующим именем.

На мой взгляд, вам никогда не следует клонировать элемент, если вы явно не укажете, что ваша структура данных делает это.

Желаемая функциональность большинства вещей требует, чтобы фактический объект был передан в структуру данных по ссылке.

Если вы хотите сделать Node класс безопаснее, сделайте его внутренним классом графа.

Использование NodeEnvelopes или фабрик ребер / узлов звучит для меня как чрезмерный дизайн.

Вы действительно хотите вообще предоставлять метод setName() для узла?В вашем примере нет ничего, что указывало бы на то, что вам это нужно.Если вы сделаете оба ваших класса Node и Edge неизменяемыми, большинство сценариев нарушения целостности, которые вы предполагаете, станут невозможными.(Если вам нужно, чтобы они были изменяемыми, но только до тех пор, пока они не будут добавлены в график, вы могли бы обеспечить это, установив флаг isInGraph в ваших классах узлов / ребер, для которого Graph установлено значение true.Добавьте{Node, Edge}, и пусть ваши мутаторы выдадут исключение, если оно будет вызвано после установки этого флага.)

Я согласен с jhkiley в том, что передача объектов Node конструктору Edge (вместо строк) звучит как хорошая идея.

Если вам нужен более навязчивый подход, вы могли бы иметь указатель из класса Node обратно на график, в котором он находится, и обновлять график, если какие-либо критические свойства (например, имя) узла когда-либо изменятся.Но я бы не стал этого делать, если вы не уверены, что вам нужно иметь возможность изменять имена существующих узлов при сохранении граничных связей, что кажется маловероятным.

Object.clone() имеет несколько серьезных проблем, и в большинстве случаев его использование не рекомендуется.Пожалуйста, смотрите пункт 11 из раздела "Эффективная Java" Джошуа Блох за полный ответ.Я считаю, что вы можете безопасно использовать Object.clone() для массивов примитивного типа, но помимо этого вам нужно быть осмотрительным в отношении правильного использования и переопределения clone.Вероятно, вам лучше определить конструктор копирования или статический фабричный метод, который явно клонирует объект в соответствии с вашей семантикой.

В дополнение к комментариям @jhkiley.blogspot.com, вы можете создать фабрику для ребер и узлов, которая отказывается создавать объекты с именем, которое уже использовалось.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top