2009-03-05 104 views
5

我現在遇到過這麼幾次了,我想知道什麼是解決循環引用的OO方法。我的意思是,甲級擁有B級會員,而乙級會員級別爲A級。如何解決OOP中的交叉引用問題?

這樣做的一個例子是將Person配偶作爲成員的Person類。

Person jack = new Person("Jack"); 
Person jill = new Person("Jill"); 
jack.setSpouse(jill); 
jill.setSpouse(jack); 

另一個例子是將某些其他產品集合作爲成員的產品類。例如,這個集合可以是那些對這個產品感興趣的人也可能感興趣的產品,並且我們想要在每個產品基礎上維護這個列表,而不是在相同的共享屬性上(例如,我們不想僅顯示所有其他同類產品)。

Product pc = new Product("pc"); 
Product monitor = new Product("monitor"); 
Product tv = new Product("tv"); 
pc.setSeeAlso({monitor, tv}); 
monitor.setSeeAlso({pc}); 
tv.setSeeAlso(null); 

(這些產品只是單純的點,這個問題是不是用是否某些產品將相互關聯的)

這會是糟糕的設計在面向對象有什麼看法?所有的面嚮對象語言是否會允許這樣做,還是僅僅是不好的做法?如果這是不好的做法,解決這個問題最好的方法是什麼?

+0

@Jon Limjap:感謝您糾正錯字。 – tehvan 2009-03-05 08:18:04

回答

1

這不是OO設計中的根本問題。例如,它可能成爲問題的一個例子是圖遍歷,例如,找到兩個對象之間的最短路徑 - 您可能會陷入無限循環。但是,這是您必須逐個考慮的問題。如果你知道在這種情況下可能存在交叉引用,那麼編寫一些檢查以避免無限循環(例如,維護一組訪問節點以避免重新訪問)。但是,如果沒有理由可能會成爲問題(例如您在問題中給出的例子),那麼擁有這樣的交叉引用並不差。正如你所描述的,在很多情況下,這是解決手頭問題的好辦法。

2

你給的例子是(對我來說)合理的面向對象設計的例子。

您所描述的交叉引用問題不是任何設計過程的人爲因素,而是您作爲對象呈現的事物的現實生活特徵,所以我沒有看到存在問題。

你遇到過什麼讓你覺得這種方法是壞設計的印象?

更新3月11日:

在缺少垃圾收集,其中內存管理明確管理系統,一個常見的做法是,要求所有的對象有一個所有者 - 一些其他的對象負責管理該對象的生命週期。

一個例子是Delphi的TComponent類,它提供級聯支持 - 銷燬父組件,並且所有擁有的組件也被銷燬。

如果你這樣一個系統的工作,在這個問題上描述的參考環的種類可以被認爲是糟糕的設計,因爲沒有明確的所有者,沒有一個對象負責管理壽命。

我在某些系統中看到的處理方式是保留引用(因爲它們正確地捕獲業務問題),並添加一個顯式的事務處理對象,該對象擁有加載到業務域的所有內容數據庫。此上下文對象負責瞭解哪些對象需要保存,並在處理完成時清理所有內容。

+0

對我來說,這似乎也是現實生活中,我只是想,虛擬機不會迷惑追逐對象......加載一個人,加載該人的配偶,再次加載該人的配偶......這是一個永恆的循環。只是想知道所有的虛擬機是否可以正確處理。 – tehvan 2009-03-05 07:00:05

+0

VM?你的意思是運行時間?僅Ref-counting系統(例如COM或roll-your own智能指針)將會,任何真正的GC系統都不會 – 2009-03-05 08:33:51

1

這是一個問題的主要時間是,如果它變得太混亂,不能應付或維護,因爲它可以成爲一種意大利麪代碼的形式。

但是,要觸及你的例子;

另請參見是完全有效如果這是一個功能你在你的代碼所需要的 - 它是一個指針(或引用)到其他項目的簡單列表中的用戶可能感興趣的

相若方式是。完全有效的添加配偶,因爲這是一個簡單的真實世界的關係,不會讓維護你的代碼的人感到困惑。

我一直把它看作是潛在的代碼異味,或者是一個警告,讓我退後一步,理順我在做的事情。

對於一些在代碼中發現遞歸關係的系統(在上面的評論中提到過),無論這種設計如何,這些都可以出現。我最近研究了一個具有遞歸「關係類型」的元數據捕獲系統,即列在邏輯上與其他列相關。它需要由試圖解析你的系統的代碼來處理。

-2

解決此問題的一種方法是通過id引用其他對象。

例如

Person jack = new Person(new PersonId("Jack")); 
Person jill = new Person(new PersonId("Jill")); 
jack.setSpouse(jill.getId()); 
jill.setSpouse(jack.getId()); 

我不是說這是一個完美的解決方案,但它會阻止循環引用。您正在使用對象而不是對象引用來建模關係。

0

我不認爲這樣的循環引用是一個問題。

但是,將所有這些關係放入對象中可能會增加太多混亂,所以您可能反而想在外部表示它們。例如。您可以使用散列表來存儲產品之間的關係。

1

我不認爲這是一個交叉引用的例子。

交叉引用通常涉及這種情況下:

class A 
{ 
    public void MethodA(B objectB) 
    { 
     objectB.SomeMethodInB(); 
    } 
} 

class B 
{ 
    public void MethodB(A objectA) 
    { 
     objectA.SomeMethodInA(); 
    } 
} 

在這種情況下,每個對象種「到達」彼此; A呼叫B,B呼叫A,並且他們變得緊密耦合。如果A和B位於不同的包/名稱空間/程序集中,這會變得更糟;在很多情況下,這些會在線程編譯程序集時產生編譯時錯誤。

解決該問題的方法是讓任一對象都可以實現所需方法的接口。

你的情況,你只有「達到在」一個級別:

public Class Person 
{ 
    public void setSpouse(Person person) 
    { ... } 
} 

我不認爲這是不合理的,也不是相互參照/循環引用,甚至一個情況。

0

引用其他對象根本不是一個真正糟糕的OO設計。這是在每個對象內管理狀態的方式。

一個很好的經驗法則是德米特法則。看看LoD(Paperboy和錢包)的這篇完美論文:click here