Java Swing设计指南

时间:2013-09-25 10:33:00

标签: java swing design-patterns user-interface

我编写了一个使用Swing for GUI的应用程序,通过GUI接受文件,解析Input,将其保存在DataList中并将其发送到服务器。我关心我的程序的整个设计,我认为这不是很好。我正在使用Netbeans来设计GUI,并且有一个类MainClass,它启动该GUI并具有对GUI的静态引用。还有一些ExecClasses执行上述解析和发送数据。

                 +----------------------+
                 | MainClass (static)   |
                 |----------------------|
          +------+  -DataList           +-----+
          |      |                      |     |
    static|      +-+--------------+-----+     |static
  reference        |              |           |reference
          |        |new ()        | new ()    |
          |        |              |           |
          |        |              |           |
        +-+--------v----+      +--v-----------+--+
        |               |      |                 |
        | SwingGUIClass |      | ExecClasses     |
        |               |      |                 |
        +--/\-----------+      +-----------------+
           |
          Input file

此处简要概述了MainClass

public class MainClass {

    private static MainClass mainClass;
    private static ExecClass1 ex1;
    private static ExecClass2 ex2;
    private static ExecClass3 ex3;

  public static void startExecClass2(String param){

     ex2 = new ExecClass2(param);
  }

我正在使用这些引用,以便SwingGUIClass可以执行ExecClass1中的方法。我之所以选择这种方法是因为我有一个TextArea需要从其中一个ExecClasses中获取数据并将其显示在GUI中。因为我无法从ExecClass修改TextArea。

public class SwingGUIClass {

  [...]
  private void ButtonActionPerformed(java.awt.event.ActionEvent evt) { 

  Label.setText(MainClass.getList());
}
  private void Button2ActionPerformed(java.awt.event.ActionEvent evt) { 

   MainClass.startExecClass2(Button2.getText());
}

我知道这远非伟大的设计,并没有遵循一些良好的实践指南,例如MVC。所以我的问题是:你会如何设计这个以及你能给我哪些一般指示?

1 个答案:

答案 0 :(得分:7)

首先,不要在事物上做静态引用的逻辑,你可以在多个上下文中使用。例如,将来,您可以要求GUI界面的多个窗口与您的多个服务实例进行交互。

此外,你正在扼杀可测试性。

单独处理GUI和应用程序逻辑 - 不要在您的应用程序逻辑(exec类)中考虑GUI文本字段等。只需考虑输入和输出,并提供一个相互通信的类(控制器)。您可以向控制器中的应用程序逻辑提供数据,获取结果并在GUI中显示,如:

public void processFile( SomeInputFromGui input ) {
 SomeResult result = applicationLogicObject.process( input );
 guiObject.showResult( result );
}

您的组件应松散耦合,因此您可以重复使用并测试它们。您可以通过简单的依赖注入来实现这一点,例如将依赖项放在contructors / setter中:

public void initApplication() {
  AppLogic logic = new AppLogic();
  AppWindow window = new AppWindow();
  AppController controller = new Controller( logic , window );
}

这是控制器初始化方法的非常简单的草案。有了它,您可以在其他地方测试/重用您的逻辑或GUI,如单元测试。

要从您的窗口移动业务逻辑,所有事件都被触发(按钮等),您可以创建一个界面,该界面将适用于您的窗口:

public interface ProcessingController {
public void processFile( File x );
public void checkIntegrity();
public SomeDataValues getCurrentDataValues();
}

您可以在控制器(implements)中实现此逻辑,并将其用作GUI事件接收器:

window.setProcessingController( controller );

...

private void ButtonActionPerformed(java.awt.event.ActionEvent evt) { 
   processingController.processText( jMyTextField.getText() );
}

现在你可以与窗口和控制器进行双向通信。

这些是基本要点,它们为您提供了可测试性和能力,可以根据需要制作尽可能多的逻辑/控制器/窗口。此外,您还有松耦合组件:您可以为测试目的注入几乎空的AppLogic存根,或者伪造AppWindow以模拟测试中的用户操作。当然,要替换组件,您应该提取干扰并提供特定的实现:

SwingAppWindow implements ApplicationUserInterface { ...
SQLDataManager implements ApplicationDataLogic { ...
BasicController implements ProcessingController { ...

当然,您可以将其进一步拆分为单独的数据访问和商务逻辑

并记住你所有的gui动作(事件,更新)都应该在swing事件线程中运行,所以你应该使用SwingUtils,因为 swing不是线程安全的

SwingUtilities.invokeLater(new Runnable() {
    public void run() {
      .... queued action that changes the gui ...
    }
  });

请记住,不要在逻辑类中使用new对对象即时进行硬编码,例如,不要在控制器中设置new Windownew ApplicationDataModel - 因为您无法测试控制器独立或重复使用不同的逻辑/窗口实现 - 您可以创建一些类只是为了准备您的应用程序依赖项(创建组件并链接它们)和“启动它” - 它通常被称为工厂。 / p>

如果您的逻辑会变得更加复杂,请将其拆分为更多服务对象,您可以使用命令模式在gui中生成命令并对其进行处理(例如在线程安全队列中)在应用程序服务中 - 这也是撤消/重做能力的良好起点。

最后一件事 - 如果你有任何长时间运行的处理任务(即使花了1秒我们可以说它已经运行很长时间),记住直接调用它或在swingUtils中会冻结你的gui,所以对于lenghty操作创建单独的具有Thread,Executors,Runnable,SwingWorker等的线程(您可以使用观察者模式来监视进度等)。

请记住,这确实是一个很大的话题,本文仅提及一些小的一般性建议。

要采取的“其他方法”可以是使用已经提供的架构来创建Eclipse RCP或Netbeans Platform等GUI应用程序。