如何在不使用Spring等时将Swing GUI与Business Logic分开

时间:2014-06-15 10:02:10

标签: java swing oop decoupling cohesion

请注意,这是一篇很长的帖子。对不起,但我想澄清一点:

我想知道如何在很长一段时间内将Swing GUI与Presentation和Business Logic分开。 在工作中,我必须使用一个小的Swing对话框为一些数据实现3 MD Excel Export以配置导出。 我们不使用像Spring这样的框架,所以我必须自己实现它。

我想完全将GUI与Business Logic分开,后者正在执行精确的后续任务:

  • 告诉BL从GUI开始工作
  • 报告从BL到GUI的进度
  • 报告从BL到GUI的记录
  • 将BL结果委托给GUI
当然,GUI不应该注意BL实现,反之亦然。 我为上述所有任务创建了几个接口,例如: G。 ProgressListenerLogMessageListenerJobDoneListener, 等等,由Business Logic解雇。例如,如果业务逻辑想要告知记录,则调用

fireLogListeners("Job has been started");

实现公共接口LogListener +的类附加到BL,现在将通知"作业已经启动"日志消息。 所有这些监听器此时都是由GUI本身实现的,通常如下所示:

public class ExportDialog extends JDialog implements ProgressListener, LogListener, JobFinishedListener,  ErrorListener {

    @Override
    public void jobFinished(Object result){
        // Create Save File dialog and save exported Data to file.
    }

    @Override
    public void reportProgress(int steps){
        progressBar.setValue(progressBar.getValue()+steps);
    }

    @Override
    public void errorOccured(Exception ex, String additionalMessage){
        ExceptionDialog dialog = new ExceptionDialog(additionalMessage, ex);
        dialog.open();
    }

    // etc.
}

" GUI和BL创建类"只需将GUI(作为所有这些监听器'接口)附加到BL,看起来像这样:

exportJob.addProgressListener(uiDialog);
exportJob.addLogListener(uiDialog);
exportJob.addJobFinishedListener(uiDialog);
exportJob.start();

我现在对此非常不确定,因为它看起来很奇怪,因为所有新创建的侦听器接口。 你有什么想法? 如何将Swing GUI组件与BL分开?

修改 为了更好的演示目的,我在eclipse中创建了一个Demo工作区file-upload.net/download-9065013/exampleWorkspace.zip.html 我也把它粘贴到了pastebin,但最好在eclipse中导入这些类,相当多的代码http://pastebin.com/LR51UmMp

2 个答案:

答案 0 :(得分:2)

一些事情。

我在ExportFunction类中没有uiDialog代码。整个执行方法应该只是主类中的代码。 ExportFunctions的责任是'导出'而不是'show gui'。

public static void main(String[] args) {
    ExportFunction exporter = new ExportFunction();

    final ExportUIDialog uiDialog = new ExportUIDialog();
    uiDialog.addActionPerformedListener(exporter);

    uiDialog.pack();
    uiDialog.setVisible(true);
}

(不需要Swing.invokeLater())

你好像过度工程了。我不知道为什么你会期望有很多线程同时运行。当您按下按钮时,您只希望一个线程正确运行?然后就不需要有一个actionPerformedListener数组。

而不是:

button.addActionListener(new ActionListener() {

    @Override
    public void actionPerformed(ActionEvent arg0) {
        if (startConditionsFulfilled()) {
            fireActionListener(ActionPerformedListener.STARTJOB);
        }
    }

});

为什么不呢:

final ExportJob exportJob = new ExportJob();
exportJob.addJobFinishedListener(this);
exportJob.addLogListener(this);

button.addActionListener(new ActionListener() {

    @Override
    public void actionPerformed(ActionEvent e) {
        exportJob.start();
    }
});

这样你就可以摆脱出真正用于任何目的的ExportFunction。

您似乎拥有大量的侦听器数组。除非你真的需要它们,否则我不会打扰它们并保持尽可能简单。

而不是:

Thread.sleep(1000);
fireLogListener("Excel Sheet 2 created");
Thread.sleep(1000);

刚才:

Thread.sleep(1000);
log("Excelt Sheet 1 created");
Thread.sleep(1000);

其中log为:

private void log(final String message) {
    ((DefaultListModel<String>) list.getModel()).addElement(message);
}

这样你就可以保持简单和清洁。

GUI不应该知道BL,但BL不知何故必须告诉GUI该做什么。您可以使用大量接口抽象无限制,但在99.99%的应用程序中,这不是必需的,尤其是您的应用程序看起来相当简单。

因此,虽然您编写的代码非常好,但我会尝试简化并减少接口。它不能保证那么多工程。

答案 1 :(得分:2)

基本上,您的架构对我来说似乎没问题。我想你是否想知道是不是因为你设置了众多的听众。

解决方法可能是:

a)拥有一个通用的Event类,其中包含特定事件的子类。 您可以使用访问者来实现实际的侦听器。

b)使用事件总线(例如,参见番石榴)。 使用事件总线体系结构,您的模型将事件发布到事件总线, 您的UI对象将侦听事件总线中的事件,并对其进行过滤。

有些系统甚至可以使用注释来声明侦听器方法。