2

我正在开发一个具有多个子系统的 java swing 应用程序。出于所有意图和目的,假设我正在制作一个带有随机附加功能的互联网聊天程序。该功能将是......一个调度程序,您可以在其中设置时间并在该时间收到提醒,并通知您朋友列表中的每个人您收到提醒。

将此功能组织成三个类是有意义的:GUI、ChatManager 和调度程序。这些类将执行以下操作:

GUI - 定义所有摇摆组件和事件
ChatManager - 创建聊天连接,发送和接收消息,管理好友列表
调度程序- 监控系统时间,发送通知,存储文件以记住会话之间的事件

为了使程序正常工作,这些类中的每一个都必须能够与其他两个类进行通信。GUI 需要告诉 ChatManager 何时发送消息并告诉 Scheduler 何时开始监控。ChatManager 需要在收到消息时在 GUI 上显示消息,最后,Scheduler 需要在完成时通知 GUI,并向 ChatManager 发送状态更新或其他任何内容。

当然,这里描述的类都非常简单,让它们直接相互通信可能不是一个坏主意。但是,为了这个问题,我们假设交互要复杂得多。

例如,假设我们可以向调度程序注册特定事件而不是特定时间。发送消息时,我将消息发送给用户,将其存储在日志文件中,创建事件对象并将其传递给调度程序,并处理沿途可能引发的任何异常。

当通信变得如此复杂时,如果与这些类的通信可能发生在许多不同的地方,那么维护代码就变得很困难。例如,如果我要重构 ChatManager,我可能还需要对 GUI 和调度程序(以及其他任何东西,如果我引入新的东西)进行重大更改。这使得代码难以维护,并使我们睡眠不足的程序员在进行更改时更有可能引入错误。

最初似乎最有意义的解决方案是使用调解器设计模式。这个想法是这三个主要类都没有直接相互感知,相反,每个类都感知到一个中介类。反过来,中介类定义了处理三个类之间通信的方法。因此,例如,GUI 将调用中介类中的 sendMessage() 方法,中介将处理需要发生的所有事情。最终,这将三个主要类解耦,对其中一个类的任何更改都可能只会导致对中介的更改。

然而,这带来了两个主要问题,最终导致我来到这里寻求反馈。它们如下:

问题

  1. 许多任务需要更新 GUI,但中介者不知道这些组件。- 假设用户启动程序并输入他们的用户名/密码并单击登录以登录到聊天服务器。登录时,您希望通过在登录屏幕上显示文本来报告登录过程,例如“正在连接...”、“正在登录...”或“错误”。如果您在 Mediator 类中定义登录方法,那么显示这些通知的唯一方法是在 GUI 类中创建一个公共方法来更新正确的 JLabel。最终,GUI 类将需要大量方法来更新其组件,例如显示来自特定用户的消息、在用户登录/注销时更新您的朋友列表等等。此外,您必须预料到这些 GUI 更新可能随时随机发生。可以吗?

  2. Swing 事件调度线程。您将主要从在 EDT 上执行的组件 ActionListener 调用中介方法。但是,您不想在 EDT 上发送消息或读/写文件,否则您的 GUI 将变得无响应。因此,在中介对象中提供一个 SingleThreadExecutor 是否是个好主意,中介对象中的每个方法都定义一个可以提交给执行线程的新可运行对象?此外,更新 GUI 组件必须在 EDT 上进行,但该 Executor 线程将调用方法来更新 GUI 组件。因此,GUI 类中的每个公共方法都必须将自身提交给 EDT 以执行。那有必要吗?

对我来说,在 GUI 类中有一个方法来更新每个以某种方式与外部通信的组件似乎需要做很多工作,其中每个方法都有额外的无意中检查它是否在 EDT 上,并将自身添加到EDT 否则。此外,Mediator 类中的每个公共方法都必须执行类似的操作,要么将 Runnable 代码添加到 Mediator 线程,要么启动一个工作线程。

总体而言,使用 Mediator 模式维护应用程序的工作量似乎几乎与不使用它维护应用程序的工作量一样多。所以,在这个例子中,如果有的话,你会做些什么不同的事情?

4

4 回答 4

3
  1. 您的 GUI 类最终会使用许多方法来使其保持最新状态,这很好。如果它让您担心,总可以选择将 GUI 分解为子 GUI,每个子 GUI 具有不同的功能或一小组相关功能。方法的数量显然不会改变,但会更有条理、连贯和解耦。

  2. 不要让 GUI 中的每个方法都创建一个 Runnable 并使用 SwingUtilities.invokeLater 将该更新放在 EDT 上,我建议您尝试另一种解决方案。对于我的个人项目,我使用Swing 应用程序框架(JSR296),它有一些方便的任务类来启动后台作业,然后成功方法自动在 EDT 线程上。如果您不能使用它,您应该尝试为后台作业创建自己的类似框架。

于 2009-06-09T08:52:34.847 回答
1

好吧,我会改变你正在工作的世界。你有 3 个班级,每个班级都只是聊天世界的观察者。MVC _是如何处理你的问题的方法。您必须为您的世界创建模型,在本例中为聊天程序。该模型将存储数据、聊天队列、好友列表并关注一致性并通知所有对更改感兴趣的人。此外,还会有几个观察者对世界状态感兴趣,并将其状态反映给用户和服务器。GUI 将可视化带到朋友列表和消息队列中,并对他们的变化做出反应。调度程序正在查看计划任务的更改并使用其结果更新模型。ChatManager 将在 SessionManager、MessageDispatcher、MessageAcceptor 等几个类中更好地完成其工作。您有 3 个具有空中心的类。创建中心并使用此中心和观察者模式将它们连接在一起. 然后每个班级将只处理一个班级并且只处理有趣的事件。一个 GUI 类是个坏主意。划分为更多代表逻辑组的子类(模型视图)。这是征服你的 UI 的方式。

于 2009-06-09T14:37:23.073 回答
1

在这里,您的设计问题的部分答案......

看起来您希望组件之间具有松散耦合。在您的情况下,我将使用调解器作为 GUI 的消息调度程序。

ChatManager 和调度程序将生成 UpdateUIMessage。

我会这样写我的GUI

public class MyView {

    public void handleUpdateMessage(final UpdateUIMessage msg){
        Runnable doRun = new Runnable(){
            public void run(){
                handleMessageOnEventDispatcherThread(msg);
            }
        };
        if(SwingUtilities.isEventDispatcherThread()){
            doRun.run();
        } else {
            SwingUtilities.invokeLater(doRun);
        }
    }
}

所以你的 GUI 上只有一个公共方法,它处理所有的 EdT 东西。

如果你想在 GUI 和其他组件之间有一个松散的耦合(意思是:你不希望 GUI 知道其他组件的所有 API),GuiController 也可以发布 ActionMessage(在特定的线程上?),这将由中介分派给其他组件。

希望能帮助到你。

于 2009-06-09T08:38:50.787 回答
1

您可能希望查看最初作为 Flex 开发的 MVC 框架开始的项目。同时, PureMVC已被移植到许多编程语言,包括 Java。尽管在撰写本文时它仅处于 alpha 状态!

于 2009-06-09T15:35:51.320 回答