2008-09-15 27 views
2

我想在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類,將維持圖形結構的完整性。這是做這件事的正確方式還是設計從一開始就被打破?

回答

4

我在Java中使用圖形結構很多,我的建議是將圖形依賴的Node和Edge類的任何數據成員保存在最終結構中,而不需要任何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); 
    } 
} 

如果您需要修改一個節點的名稱,刪除舊的節點並添加一個新的。這可能聽起來像是額外的工作,但它可以節省很多努力,保持一切順利。但是,真的,從頭開始創建自己的Graph結構可能是不必要的 - 這個問題只是您創建自己的許多時可能遇到的第一個問題。

我會建議找一個好的開源Java圖庫,然後用它來代替。根據你在做什麼,這裏有幾個選項。我過去曾使用過JUNG,並建議將它作爲一個很好的起點。

1

在我看來,你不應該克隆元素,除非你明確聲明你的數據結構是這樣做的。

大多數事物的所需功能需要通過參考將實際對象傳遞到數據結構中。

如果您想讓Node類更安全,請將其作爲圖的內部類。

+0

我已節點類內,並暴露其外面使用的接口。節點對象的任何更改也會更新圖形結構。你可以看到在我的博客的源代碼:http://dev.spartancoder.com/?q=graph-handling-class-project-graph-studio – 2008-09-16 13:42:27

3

我不明白爲什麼要爲節點添加額外的字符串名稱間接尋址。難道你的Edge構造函數的簽名更像是public Edge(String, Node, Node)而不是public Edge (String, String, String)

我不知道克隆會在哪裏幫助你。 ETA:如果在創建節點後節點名稱發生改變而產生危險,如果客戶端嘗試使用現有名稱在節點上調用setName(),則會拋出IllegalOperationException

+0

我認爲這是同樣的事情。這不會解決我上面說明的問題。如果在添加節點後更改名稱,我仍然可以擁有兩個具有相同名稱的節點。 – 2008-09-15 15:22:59

0

除了@ jhkiley.blogspot.com的評論外,您還可以爲邊緣和節點創建一個工廠,以拒絕創建已使用名稱的對象。

1

使用NodeEnvelopes或邊緣/節點工廠聽起來像過度設計給我。

你真的想在節點暴露出的setName()方法呢?您的示例中沒有任何建議您需要這樣做。如果您將Node和Edge類都設爲不可變,那麼您設想的大多數完整違例場景都變得不可能。 (如果你需要他們可變的,但只有等到他們加入到一個圖形,可以通過具有在由Graph.Add {節點,邊緣}設置爲true的節點/邊緣類的isInGraph標誌強制執行這一點,有你的存取器拋出一個異常,如果該標誌被設置後調用。)

我同意jhkiley是通過節點對象邊緣的構造函數(而不是字符串)聽起來是個好主意。

如果您需要一種更加侵入式的方法,您可以從節點類指向它駐留的圖形,並在節點的任何關鍵屬性(例如名稱)發生更改時更新圖形。但是我不會那樣做,除非您確定需要能夠更改現有節點的名稱,同時保留Edge關係,這似乎不大可能。

1

Object.clone()有一些重大問題,它的使用在大多數情況下氣餒。請參閱Joshua Bloch的「Effective Java」中的第11項以獲取完整答案。我相信你可以在原始類型數組上安全地使用Object.clone(),但除此之外,你需要明智地正確使用和覆蓋克隆。你最好定義一個拷貝構造函數或一個靜態工廠方法,根據你的語義顯式地克隆這個對象。

相關問題