【问题标题】:Java Swing design pattern for complex class interaction用于复杂类交互的 Java Swing 设计模式
【发布时间】:2013-01-18 20:39:08
【问题描述】:

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

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

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 模式维护应用程序的工作量似乎几乎与没有它的应用程序一样多。那么,在这个例子中,如果有什么不同的话,你会做些什么呢?

【问题讨论】:

    标签: java swing edt


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

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

    【讨论】:

    • 将 GUI 拆分为多个类并让中介器与 GUI 控制器通信听起来是个好主意。它并没有减少方法的数量,但实际上它在很多方面使它更有条理。 SwingWorker API(我相信包含在 JDK6 中)提供了一个方便的接口,用于在线程生命周期期间或之后在 EDT 上启动后台线程和执行方法。当某些长时间运行的任务完成时,让中介使用这些可以简单地通知 GUI。
    【解决方案2】:

    这里,部分回答你的设计问题...

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

    ChatManager 和 Scheduler 会生成 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(在特定的线程上? ),这将由中介分派给其他组件。

    希望对你有帮助。

    【讨论】:

      【解决方案3】:

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

      【讨论】:

      • MVC 是一个很棒的模式。特别是,我认为在处理多个 GUI 类时这将是一个明智的设计选择。然而,GUI 不只是观察聊天,它还可以观察调度程序和任何数量的其他类。此外,这些类正在观察 GUI,并相互观察。你必须创建很多不同类型的观察者。您还可以采用命令方法并将所有事件封装在一个通用对象中,但解析可能会对性能造成很大影响。
      • 好吧,我看你没听懂我的回答。真正的力量是制作掌握世界现状的模型。然后是这个模型的观察者和修改模型状态的控制器。您将类之间的交互降低到 Controller/Observer -> Model。无需在 C1/O1 C2/O2 之间创建交互。
      【解决方案4】:

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-05-13
        • 1970-01-01
        • 1970-01-01
        • 2014-08-26
        • 1970-01-01
        • 2012-08-19
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多