7

请注意,这是一个很长的帖子。很抱歉,但我想澄清一下:

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

我想将 GUI 与业务逻辑完全分开,它们的任务如下:

  • 告诉 BL 从 GUI 开始它的工作
  • 从 BL 向 GUI 报告进度
  • 从 BL 到 GUI 的报告记录
  • 将 BL 结果委托给 GUI

当然 GUI 不应该有 BL 实现的通知,反之亦然。我为上面的所有任务创建了几个接口,例如 a ProgressListenerLogMessageListenerJobDoneListener等,由业务逻辑触发。例如,如果业务逻辑想要讲述日志记录,它会调用

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 file-upload.net/download-9065013/exampleWorkspace.zip.html 中创建了一个演示工作区,我也将它粘贴到了 pastebin,但最好在 eclipse 中导入这些类,很多代码http: //pastebin.com/LR51Ummp

4

2 回答 2

2

一些东西。

我不会在 ExportFunction 类中有 uiDialog 代码。整个 perform 方法应该只是主类中的代码。ExportFunctions 的职责是“导出”而不是“显示 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);

日志是:

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

这样你就可以让它更简单、更干净。

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

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

于 2014-06-15T14:32:47.760 回答
2

基本上,您的架构对我来说似乎还可以。我想您想知道是不是因为您设置了众多听众。

对此的解决方案可能是:

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

b) 使用事件总线(例如,参见 guava)。使用事件总线架构,您的模型会将事件发布到事件总线,而您的 UI 对象将侦听来自事件总线的事件并过滤它们。

一些系统甚至可以使用注解来声明监听器方法。

于 2015-04-22T15:32:53.977 回答