Должен ли я использовать клон при добавлении нового элемента? Когда следует использовать клон? - PullRequest
2 голосов
/ 15 сентября 2008

Я хочу реализовать в 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, который будет поддерживать структурную целостность графа. Это правильный способ сделать это, или дизайн не работает с самого начала?

Ответы [ 6 ]

4 голосов
/ 15 сентября 2008

Я много работаю со структурами графов в Java, и я бы советовал сделать любой элемент данных класса Node и Edge, от которого зависит Graph, для поддержания своей структуры окончательным, без установщиков. На самом деле, если бы вы могли, я бы сделал 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 с открытым исходным кодом и использовать ее вместо этого. В зависимости от того, что вы делаете, есть несколько вариантов. Я использовал JUNG в прошлом и рекомендовал бы это как хорошую отправную точку.

3 голосов
/ 15 сентября 2008

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

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

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

1 голос
/ 10 декабря 2008

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

1 голос
/ 15 сентября 2008

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

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

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

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

1 голос
/ 15 сентября 2008

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

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

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

0 голосов
/ 15 сентября 2008

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...