2008-09-15 15 views
2

グラフデータ構造を扱うためのクラスをJavaで実装したいと思います。 NodeクラスとEdgeクラスがあります。 Graphクラスは、ノードのリストとエッジのリストの2つのリストを保持します。各ノードには一意の名前が必要です。どのように私はこのような状況を防ぐん: 新しい要素を追加するときにクローンを使用する必要がありますか?クローンをいつ使うべきですか?

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クラスを返すべきであると考えています。これは正しいことでしょうか、あるいはデザインが最初から壊れていますか?

答えて

4

私はJavaで多くのグラフ構造を扱いますが、私の助言は、構造体を最終的に維持するためにsetterを使わずにグラフが依存するNodeクラスとEdgeクラスのデータメンバを作ることです。実際、可能であれば、私はノードとエッジを完全に不変にします。これはmany benefitsです。

ので、例えば:

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 
} 

その後、グラフオブジェクトであなたの一意性チェックを行うだろう:あなたはノードの名前を変更する必要がある場合

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を使用しており、それを良い出発点として推奨します。

1

私の意見では、データ構造が明示的に述べていない限り、要素を複製するべきではありません。

ほとんどのものの望ましい機能には、参照によってデータ構造に渡される実際のオブジェクトが必要です。

Nodeクラスをより安全にするには、グラフの内部クラスにします。

+0

ノードクラスをinnerにして、インターフェイスを使って外部に公開しました。ノードオブジェクトの変更は、グラフ構造を更新します。私のブログでソースコードを見ることができます:http://dev.spartancoder.com/?q=graph-handling-class-project-graph-studio –

3

ノードの文字列名の間接参照を追加する理由はわかりません。あなたのEdgeコンストラクタのシグネチャがpublic Edge (String, String, String)の代わりにpublic Edge(String, Node, Node)のようなものになるのは意味がありませんか?

クローンがどこに役立つか分かりません。

ETA:ノードの作成後にノード名を変更した場合、クライアントが既存の名前のノードでsetName()を呼び出そうとすると、IllegalOperationExceptionがスローされます。

+0

私はそれが同じことだと思います。これは私が上に示した問題を解決しません。ノードが追加された後に名前が変更された場合でも、同じ名前の2つのノードを持つことは可能です。 –

+0

IllegalOperationExceptionをスローすると、良いアイデアのように聞こえるが、私はまだ改善されたクラスデザインのソリューションが最初からあると思う。私はまだ考えている。 –

0

@ jhkiley.blogspot.comのコメントに加えて、すでに使用されている名前のオブジェクトの作成を拒否するエッジとノード用のファクトリを作成できます。

1

NodeEnvelopesまたはエッジ/ノードファクトリを使用すると、私には過剰設計のように聞こえます。

NodeでsetName()メソッドを公開したいのですか?あなたの例には、それが必要であることを示唆するものは何もありません。 NodeクラスとEdgeクラスの両方を不変にすると、想定している整合性違反のシナリオのほとんどが不可能になります。 (変更可能にする必要があるが、グラフに追加されるまでは、Graph.Add {Node、Edge}によってtrueに設定されているノード/エッジクラスにisInGraphフラグを設定することでこれを強制することができます

Nodeオブジェクトを(Stringsではなく)Edgeコンストラクタに渡すことが良いアイデアのように聞こえると思いますが、私はjhkileyに同意します。

もっと侵入的なアプローチが必要な場合は、Nodeクラスからそのグラフが存在するグラフへのポインタを持ち、ノードの重要なプロパティ(たとえば名前)が変更された場合にグラフを更新できます。しかし、Edge関係を維持しながら既存のノードの名前を変更できるようにする必要があると確信していない限り、私はそれをしません。そうは思われません。

1

Object.clone()には大きな問題があり、ほとんどの場合、その使用はお勧めしません。ジョシュア・ブロッホの「Effective Java」の項目11を参照してください。私はあなたが安全にPrimitive型の配列でObject.clone()を使うことができると信じていますが、それ以外にも、適切にクローンを使い、オーバーライドすることを賢明にする必要があります。セマンティクスに従ってオブジェクトを明示的に複製するコピーコンストラクタまたは静的ファクトリメソッドを定義する方がよいでしょう。

関連する問題