2009-02-03 45 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,而无需修改服务器。

这意味着您将无法自动发送事件,您需要手动连线。