5

我有一个程序(除其他外)有一个命令行界面,允许用户输入字符串,然后将通过网络发送。问题是我不确定如何将在 GUI 深处生成的事件连接到网络接口。例如,假设我的 GUI 类层次结构如下所示:

GUI -> MainWindow -> CommandLineInterface -> EntryField

每个 GUI 对象都包含其他一些 GUI 对象,并且一切都是私有的。现在 entryField 对象生成一个事件/信号,表明已输入了一条消息。目前我将信号向上传递到类层次结构中,因此 CLI 类看起来像这样:

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

在 c'tor 中:

entryField.msgEntered.connect(sigc::mem_fun(this, &CLI::passUp));

passUp 函数只是再次发出信号让所属类(MainWindow)连接到,直到我终于可以在主循环中执行此操作:

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 上遇到了几乎相同的问题。这真的是一个普遍的问题。

4

6 回答 6

9

根本问题是您将 GUI 视为一个单一的应用程序,只有 gui 通过比平常更大的线连接到逻辑的其余部分。

您需要重新考虑 GUI 与后端服务器交互的方式。一般来说,这意味着您的 GUI 成为一个独立的应用程序,它几乎什么都不做,并且与服务器对话,而 GUI 内部(即您的信号和事件)与服务器的处理逻辑之间没有任何直接耦合。即,当您单击一个按钮时,您可能希望它执行一些操作,在这种情况下您需要调用服务器,但几乎所有其他事件只需要更改 GUI 内的状态而不对服务器执行任何操作 - 直到你准备好了,或者用户想要一些响应,或者你有足够的空闲时间在后台拨打电话。

诀窍是为服务器定义一个完全独立于 GUI 的接口。您应该能够稍后更改 GUI,而无需修改服务器。

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

于 2009-02-03T09:25:48.080 回答
3

试试观察者设计模式。链接包括截至目前的示例代码。

您缺少的基本内容是,如果该引用被转换为您的对象实现的接口(抽象类),您可以在不违反封装的情况下传递引用。

于 2009-02-03T09:17:58.893 回答
2

由于这是一个普遍的问题,即使我“只是”一名 Java 程序员,我也会尝试回答它。:)

我更喜欢在我的程序的两边都使用接口(抽象类或 C++ 中的任何相应机制)。一方面是包含业务逻辑的程序核心。它可以生成例如 GUI 类可以接收的事件,例如(对于您的示例)“stringReceived”。另一方面,核心实现了一个“UI listener”接口,其中包含“stringEntered”等方法。

这样,UI 就与业务逻辑完全解耦了。通过实现适当的接口,您甚至可以在核心和 UI 之间引入网络层。

[编辑] 在我的应用程序的入门类中,几乎总是有这样的代码:

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

[/编辑]

于 2009-02-03T09:17:19.910 回答
2

如果没有一些全球发布/订阅中心,您将无法避免在层次结构中向上或向下传递一些东西。即使您将侦听器抽象为通用接口或控制器,您仍然必须以某种方式将控制器附加到 UI 事件。

使用 pub/sub 集线器,您可以添加另一层间接,但仍然存在重复 - entryField 仍然显示“发布消息就绪事件”,侦听器/控制器/网络接口显示“侦听消息就绪事件”,所以有一个共同点双方都需要知道的事件 ID,如果您不打算在两个地方对其进行硬编码,则需要将其传递到两个文件中(尽管作为全局,它不会作为参数传递;这本身就是' t 任何巨大的优势)。

我已经使用了所有四种方法 - 直接耦合、控制器、侦听器和 pub-sub - 在每个后继者中,您都稍微放松了耦合,但您永远不会摆脱一些重复,即使它只是 id发布的事件。

这真的归结为差异。如果您发现需要切换到接口的不同实现,那么将具体接口抽象为控制器是值得的。如果您发现需要其他逻辑来观察状态,请将其更改为观察者。如果您需要在进程之间解耦,或者想要插入更通用的架构,pub/sub 可以工作,但它引入了一种全局状态形式,并且不适合编译时检查。

但是,如果您不需要独立更改系统的各个部分,则可能不值得担心。

于 2009-02-03T09:34:25.460 回答
0

在我看来,CLI 应该独立于 GUI。在一个MVC架构中,它应该扮演模型的角色。

我会放置一个同时管理 EntryField 和 CLI 的控制器:每次 EntryField 更改时,CLI 都会被调用,所有这些都由控制器管理。

于 2009-02-03T09:18:19.360 回答
0

您可以解耦任何 GUI 并使用模板虚拟包轻松地与消息通信。也看看这个项目

于 2015-06-12T07:33:43.517 回答