2009-02-03 70 views
3

我有一個程序(其中包括)有一個命令行界面,讓用戶輸入字符串,然後將通過網絡發送。問題是我不確定如何將在GUI內部深處生成的事件連接到網絡接口。假設例如,我的GUI類層次結構是這樣的:保持GUI分離

GUI - >主窗口 - > CommandLineInterface - > EntryField

每個GUI對象持有其他一些GUI對象,一切都是私有的。現在,entryField對象會生成一條消息已輸入的事件/信號。目前,我傳遞的信號了類層次結構,因此CLI類將是這個樣子:

public: 
    sig::csignal<void, string> msgEntered; 

而在c'tor:

​​

的passUp功能只是發出信號再次所屬的類(主窗口)來連接,直到我終於可以做這個主循環:

gui.msgEntered.connect(sigc::mem_fun(networkInterface, &NetworkInterface::sendMSG)); 

現在,這似乎是一個真正的壞的解決方案。每次我向GUI添加東西時,都必須通過類層次結構進行連接。我確實看到了幾種解決方法。我可以讓所有對象公開,這將使我只是做這個主循環:

gui.mainWindow.cli.entryField.msgEntered.connect(sigc::mem_fun(networkInterface, &NetworkInterface::sendMSG)); 

但是,這將違背封裝的想法。我也可以在整個GUI上傳遞一個對網絡接口的引用,但我想盡可能保持GUI代碼的獨立性。

感覺就像我在這裏錯過了一些重要的東西。有沒有一種乾淨的方式來做到這一點?

注意:我使用的是GTK +/gtkmm/LibSigC++,但我並沒有將它標記爲這樣,因爲我已經有了與Qt幾乎相同的問題。這確實是一個普遍的問題。

回答

2

由於缺少一些全球性的發佈/訂閱中心,您無法避免在層次結構中向上或向下傳遞某些內容。即使您將偵聽器抽象爲通用接口或控制器,您仍然必須以某種方式將控制器附加到UI事件。

使用pub/sub集線器可以添加另一個間接層,但仍然存在重複 - entryField仍然表示「發佈消息就緒事件」,並且偵聽器/控制器/網絡接口顯示「偵聽消息就緒事件」,所以有一個共同的事件ID,雙方都需要知道,如果你不打算在兩個地方對它進行硬編碼,那麼它需要被傳遞到兩個文件中(儘管作爲全局它不會作爲參數傳遞;本身並沒有什麼大的優勢)。

我已經使用了所有四種方法 - 直接耦合,控制器,監聽器和pub-sub - 並且在每個後繼中你稍稍鬆開耦合,但是你永遠不會有一些重複,即使它是僅發佈事件的ID。

它真的歸結爲差異。如果您發現需要切換到不同的接口實現,那麼將具體接口抽象爲控制器是值得的。如果您發現您需要其他邏輯觀察狀態,請將其更改爲觀察者。如果你需要在進程之間解耦,或者想插入更通用的體系結構,pub/sub可以工作,但它引入了一種全局狀態,並且不適合編譯時檢查。

但是,如果您不需要獨立更改系統的各個部分,則可能不值得擔心。

2

由於這是一個普遍的問題,我會嘗試回答它,即使我只是一個Java程序員。 :)

我更喜歡在我的程序的兩邊使用接口(抽象類或任何相應的機制在C++中)。一方面是包含業務邏輯的程序核心。它可以產生例如GUI類可以接收,例如(例如)「stringReceived」。另一方面,核心實現了一個「UI監聽器」接口,其中包含諸如「stringEntered」之類的方法。

這樣UI就完全與業務邏輯分離了。通過實施適當的界面,您甚至可以在覈心與用戶界面之間引入網絡層。

[編輯]在我的應用程序啓動器類,幾乎總是這樣的代碼:

Core core = new Core(); /* Core implements GUIListener */ 
GUI gui = new GUI(); /* GUI implements CoreListener */ 
core.addCoreListener(gui); 
gui.addGUIListener(core); 

[/編輯]

+0

不應該Core實現CoreListener和GUI實現GUIListener? 如果沒有,那麼我討厭維護你的代碼:P – 2009-02-03 09:37:08

+0

根本不是因爲核心需要響應GUI事件,GUI需要響應核心事件。保持這一點非常簡單,因爲我不必關心核心中的GUI(反之亦然)。 – Bombe 2009-02-03 09:43:25

3

嘗試Observer設計模式。鏈接包括截至目前的示例代碼。

你缺少的重要事情是,如果該引用被轉換爲對象實現的接口(抽象類),則可以傳遞引用而不違反封裝。

+0

所以你說我應該傳遞一個對我的網絡接口的參考,這是一個體面的解決方案,只要它只是一個抽象類?我觀察了維基百科上的觀察者模式,但我不確定自己會看到如何使用信號。事實上,似乎信號已經實現了它。 – drby 2009-02-03 10:04:32

0

在我看來,CLI應該獨立於GUI。在MVC架構中,它應該扮演模型的角色。

我會放置一個管理EntryField和CLI的控制器:每當EntryField發生變化時,CLI都會被調用,所有這些都由控制器管理。

8

根本問題在於,您將GUI視爲其單一應用程序,只有gui通過比平常更大的線連接到邏輯的其餘部分。

您需要重新考慮GUI與後端服務器交互的方式。通常這意味着您的GUI變成了一個獨立的應用程序,它幾乎不做任何事情,並且與服務器進行對話,而不在GUI的內部(即您的信號和事件)與服務器的處理邏輯之間直接耦合。也就是說,當你點擊一個按鈕時,你可能希望它執行一些操作,在這種情況下你需要調用服務器,但幾乎所有其他事件只需要改變GUI內部的狀態,而不對服務器做任何事情 - 直到你準備好了,或者用戶想要一些響應,或者你有足夠的空閒時間在後臺進行呼叫。

訣竅是定義一個完全獨立於GUI的服務器接口。以後應該可以更改GUI,而無需修改服務器。

這意味着您將無法自動發送事件,您需要手動連線。