Pergunta

Quero implementar em Java uma classe para lidar com estruturas de dados gráficos.Eu tenho uma classe Node e uma classe Edge.A classe Graph mantém duas listas:uma lista de nós e uma lista de arestas.Cada nó deve ter um nome exclusivo.Como posso me proteger contra uma situação 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"); 

Acredito que devo clonar os nós e as arestas ao adicioná-los ao gráfico e retornar uma classe NodeEnvelope que manterá a integridade estrutural do gráfico.Esta é a maneira correta de fazer isso ou o design está quebrado desde o início?

Foi útil?

Solução

Eu trabalho muito com estruturas de grafos em Java, e meu conselho seria tornar final qualquer membro de dados da classe Node e Edge da qual o Graph depende para manter sua estrutura, sem setters.Na verdade, se você puder, eu tornaria o Node e o Edge completamente imutáveis, o que muitos benefícios.

Então, por exemplo:

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
}

Você faria então sua verificação de exclusividade no 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);
    }
}

Se você precisar modificar o nome de um nó, remova o nó antigo e adicione um novo.Isso pode parecer um trabalho extra, mas economiza muito esforço para manter tudo em ordem.

Na verdade, porém, criar sua própria estrutura de gráfico a partir do zero é provavelmente desnecessário - esse problema é apenas o primeiro de muitos que você provavelmente encontrará se criar o seu próprio.

Eu recomendaria encontrar uma boa biblioteca de gráficos Java de código aberto e usá-la.Dependendo do que você está fazendo, existem algumas opções disponíveis.Eu tenho usado JUNGO no passado, e recomendo-o como um bom ponto de partida.

Outras dicas

Não está claro para mim por que você está adicionando o indireto adicional dos nomes de String para os nós.Não faria mais sentido que a assinatura do seu construtor Edge fosse algo como public Edge(String, Node, Node) em vez de public Edge (String, String, String)?

Não sei onde o clone o ajudaria aqui.

Hora prevista de chegada:Se o perigo vier da alteração do nome do nó após a criação do nó, lance um IllegalOperationException se o cliente tentar chamar setName() em um nó com um nome existente.

Na minha opinião, você nunca deve clonar o elemento, a menos que declare explicitamente que sua estrutura de dados faz isso.

A funcionalidade desejada da maioria das coisas precisa que o objeto real seja passado para a estrutura de dados por referência.

Se você quiser fazer o Node classe mais segura, torne-a uma classe interna do gráfico.

Usar NodeEnvelopes ou fábricas de borda/nó parece um design exagerado para mim.

Você realmente deseja expor um método setName() no Node?Não há nada no seu exemplo que sugira que você precisa disso.Se você tornar imutáveis ​​as classes Node e Edge, a maioria dos cenários de violação de integridade que você está imaginando se tornará impossível.(Se você precisar que eles sejam mutáveis, mas apenas até que sejam adicionados a um Graph, você pode impor isso tendo um sinalizador isInGraph em suas classes Node/Edge que é definido como verdadeiro por Graph.Add{Node, Edge}, e faça com que seus mutadores gerem uma exceção se forem chamados depois que esse sinalizador for definido.)

Concordo com jhkiley que passar objetos Node para o construtor Edge (em vez de Strings) parece uma boa ideia.

Se você quiser uma abordagem mais intrusiva, poderá ter um ponteiro da classe Node de volta para o Graph em que ela reside e atualizar o Graph se alguma propriedade crítica (por exemplo, o nome) do Node mudar.Mas eu não faria isso a menos que você tenha certeza de que precisa alterar os nomes dos nós existentes enquanto preserva os relacionamentos do Edge, o que parece improvável.

Object.clone() tem alguns problemas importantes e seu uso é desencorajado na maioria dos casos.Consulte o item 11, de "Java eficaz" por Joshua Bloch para uma resposta completa.Acredito que você pode usar Object.clone() com segurança em matrizes de tipo primitivo, mas, fora isso, você precisa ser criterioso ao usar e substituir corretamente o clone.Provavelmente é melhor definir um construtor de cópia ou um método de fábrica estático que clone explicitamente o objeto de acordo com sua semântica.

Além dos comentários de @jhkiley.blogspot.com, você pode criar uma fábrica para Arestas e Nós que se recusa a criar objetos com um nome que já foi utilizado.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top