2008-12-16 68 views
1

我有兩個類,每個類都需要彼此的實例才能運行。通常如果一個對象需要運行另一個對象,我喜歡在構造函數中傳遞它。但在這種情況下我不能這樣做,因爲一個對象必須在另一個之前實例化,因此第二個對象不存在傳遞給第一個對象的構造函數。如何構造兩個對象,彼此作爲參數/成員

我可以通過將第一個對象傳遞給第二個對象的構造函數,然後調用第一個對象的setter來傳遞第二個對象來解決這個問題,但這似乎有點笨拙,我想知道是否有更好的方法:

backend = new Backend(); 
panel = new Panel(backend); 
backend.setPanel(); 

我從來沒有把任何研究放入MVC;我想我正在處理一個模型(後端)和視圖或控制器(面板)。我可以從MVC獲得任何見解嗎?

+0

您的後端/數據層不需要知道任何關於您的表示層。事實上,它應該沒有UI層的概念。讓它們像這樣耦合只會導致很多問題。 – 2008-12-16 16:00:31

回答

8

現在該查看MVC了。 :-)當你有一個模型 - 視圖 - 控制器的情況時,一致認爲模型不應該知道視圖 - 控制器(MVC經常演示爲M-VC),但視圖總是知道模型。

如果模型需要告訴視圖的東西,它會通過通知它的聽衆,它可能有多個。你的看法應該是其中之一。

2

在一個循環的施工場景中,我會使用工廠類/工廠方法。我通常會將構造邏輯變爲工廠專用(使用朋友構造,包級別保護或類似方法),以確保沒有人可以在不使用工廠的情況下構建實例。

setter/constructor的使用實際上是兩個類和工廠之間的契約的一部分,所以我只是使用任何方便的。

正如已經指出的那樣,您應該嘗試找到非循環解決方案。

1

最好避免循環引用。我會親自嘗試重新思考我的對象。

+0

這個答案並不是特別有用,有些情況下孩子需要知道他的父母是誰。當你需要這樣做時,有安全的解決方案(如代理)。 – 2008-12-16 16:13:36

+0

嗯,這對我很有幫助,它證實了我的懷疑,我需要重新思考我的對象。 :)我想我要去一個MVC監聽器/事件通知類型設計,如另一個答案中所建議的。我認爲這仍然是循環的,但稍好一些。 – skiphoppy 2008-12-16 16:24:38

2

首先,與其他人在此說的相反,循環引用沒有固有的問題。例如,一個Order對象應該有一個對放置Order的人的Customer對象的引用。同樣,Customer對象有自己的訂單列表是很自然的。

在基於引用的語言(如Java或C#)中,完全沒有問題。在基於價值的語言(如C++)中,你必須小心設計它們。

這就是說,你設計的:

backend = new Backend(); 
panel = new Panel(backend); 
backend.setPanel(panel); 

它幾乎做到這一點的唯一途徑。

0
panel = new Panel(backend); 

你這樣做,在該例程類似

Public Sub Panel(ByVal BackEnd as BackEnd) 
     Me.MyBackEnd = BackEnd 
     BackEnd.MyPanel = Me 
    End Sub 

你不需要BackEnd.SetPanel

最好是使用代理服務器。代理通過引發事件將一個對象鏈接到另一個對象。父母將孩子交給代理人。當孩子需要父母時,它會在代理上調用GetRef方法。然後,代理引發父母用來將自己返回給代理的事件,然後將代理交給孩子。

使用Event/Delegate機制避免了任何循環引用問題。

所以,你必須(假設後端是「父」在這裏)

Public Sub Panel(ByVal BackEnd as BackEnd) 
     Me.MyBackEnd = BackEnd.Proxy 
     BackEnd.MyPanel = Me 
    End Sub 

    Public Property MyBackEnd() as BackEnd 
    Set (ByVal Value as BackEnd) 
     priBackEndProxy = BackEnd.Proxy 
    End Set 
    Get 
     Return priBackEndProxy.GetRef 
    End Get 
    End Property 

這裏是一個循環引用的問題更詳細的討論。雖然它專注於在Visual Basic 6.0中修復它。

Dynamic Memory Allocation

另外另一溶液聚集板和後端成另一個對象。如果兩個元素都是UI控件並且需要以協調方式運行,這很常見。

最後,就MVC而言,我推薦使用模型視圖展示器方法。

基本上你有你的表單實現一個IPanelForm接口。它向一個名爲Panel的類註冊自己,它完成所有的UI邏輯。 BackEnd應該具有面板可以在模型更改時掛鉤的事件。 Panel處理事件並通過IPanelForm接口更新表單。

  1. 用戶點擊一個按鈕

  2. 的形式傳遞到面板,該用戶點擊一個按鈕

  3. 面板處理按鈕並檢索從後端數據

  4. 面板格式數據。

  5. 面板使用IPanelForm接口在窗體上顯示數據。

0

我一直在推遲實施在這裏學到的經驗教訓,給了我足夠的時間來思考確切的正確方式來做到這一點。正如其他人所說,如果後端對象在其屬性發生更改時有明確的分隔,這絕對是一種可行的方法。它不僅解決了我在這個問題中提出的具體問題,而且還會讓這些代碼中的許多其他不良設計氣味看起來更好。實際上有很多不同的Backend類(通過我的例子中使用的泛型類名),每個類都有自己相應的Panel類。甚至還有一些地方可以將一些東西移動到其他類中,按照相同的模式將其他類對分離爲後端/面板對,並減少大量傳遞垃圾的參數。

這個答案的其餘部分將使用特定語言,因爲我使用Java。

我對「JavaBeans」並沒有太多的擔心,但是我發現以下基本的JavaBean約定對我來說非常有幫助:基本上,使用屬性的標準getter和setter。結果發現有一個JavaBean約定我不知道哪一個真的會在這裏幫助:綁定屬性。綁定屬性是通過標準獲取器和設置器可用的屬性,它們在更改時觸發PropertyChangeEvents。 [我不確定,但JavaBeans標準可能會指定所有屬性都應該是「綁定屬性」。在這一點上與我無關。請注意,通過使用BeanInfo類來定義一個JavaBean的確切接口,「標準」getter和setter可能非常不標準,但我從來不會使用它。](我選擇遵循的主要的其他JavaBean約定或在每種情況下都不合適是一個無參數的構造函數;我已經在此項目中關注它,因爲這些後端對象中的每一個都必須是可序列化的。)

我找到了this blog entry,這對於提示非常有幫助我進入綁定的屬性/ PropertyChangeEvents問題,並幫助我構建一個計劃,以便我將如何重寫此代碼。

現在我所有的後端對象都繼承了一個名爲Model的公共類,它在本系統需要的每個後端都提供了幾件事情,包括序列化支持。我將創建一個額外的類JavaBean作爲Model的超類,它將提供我需要的PropertyChangeEvent支持,並由每個Model繼承。我將更新每個模型中的setter以在調用時觸發PropertyChangeEvent。我也可能有一些JavaBean繼承了幾個類,這些類在技術上並不像這些模型那樣具有同樣的意義,但也可以將其他類註冊爲偵聽器。 JavaBean類可能不完全實現JavaBean規範;正如我所說,有幾個細節我不在乎。但對於這個項目來說這已經足夠了。這聽起來像我可以通過從java.awt.Component繼承來獲得所有這些,但這些不是我可以證明的任何組件,所以我不想這樣做。 (我也不知道可能需要什麼開銷)。

一旦每個Model都是一個JavaBean,並且支持PropertyChangeEvent,我會做很多代碼清理:當前保持對Panel的引用的模型將會是更新,小組將自己註冊爲聽衆。非常乾淨!該模型不需要知道(也不應該知道首先)當財產更新時小組應該自己調用什麼方法。

相關問題