【问题标题】:Design pattern for graphical application using JFrame使用 JFrame 的图形应用程序设计模式
【发布时间】:2015-08-15 13:13:49
【问题描述】:

我总是通过像这样扩展 JFrame 类来用 Java 编写我的图形应用程序:

public class CalculatorApp extends JFrame {
    // GUI components are declared as member variables of JFrame
    private final JLabel answerLabel;
    private final JButton sumButton;
    private final JButton divideButton;
    ...
    
    // Create and display the GUI
    public CalculatorApp() {
        setTitle("A Calculator");
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        ...
        
        // Initialize components
        answerLabel = new JLabel("Your answer will appear here");
        sumButton = new JButton("Sum");
        sumButton.addActionListener((ActionEvent e) -> {
            sum();
        });
        ...
        
        // Make visible
        pack();
        setVisible(true);
    }
    
    // Actions performed by the user; these methods have access to
    // the GUI components to grab the user input (eg. TextField.getText())
    // and then update the GUI with the result (eg. answerLabel.setText()).
    public void sum() {
        ...
    }
    public void divide() {
        ...
    }
    ...
    
    public static void main(String[] args) {
        new CalculatorApp();
    }
}

尽管最近我了解到这不是很好的做法,因为您将特定应用程序的功能放入 JFrame 中 - 例如,它确实不应该具有计算器的功能。我发现尝试使用这种方法创建具有越来越多功能的应用程序会导致代码组织得非常糟糕,因为 GUI 和输入验证码与实际应用程序的功能之间没有界限。

那么构建这样一个程序的最佳方法是什么?

我在下面列出了我能想到的唯一合理的替代方案。

处理程序

这种结构通过将 GUI 很好地放入可以创建和显示的自己的小类中,将应用程序的功能与 GUI 分开,但缺点是必须管理许多烦人的处理程序。处理程序是将应用程序功能提供给 GUI 的一种方式。这种结构也更容易转换为其他JWindows,如JDialog,因为不必更改整个类层次结构。

public class CalculatorApp {
    // Create the GUI and pass it the handlers
    public CalculatorApp() {
        new CalculatorAppGUI(
            new SumHandlerImpl(),
            new DivideHandlerImpl(),
            ...
        ).show();
    }

    // All of the application function is in the handlers
    private class SumHandlerImpl() implements SumHandler {
        public double sum(double a, double b) {
            return a + b;
        }
    }
    private class DivideHandlerImpl() implements DivideHandler {
        public double divide(double a, double b) {
            return a / b;
        }
    }
    ...

    public static void main(String[] args) {
        new CalculatorApp();
    }
}

public class CalculatorAppGUI {
    // Handlers that perform application function are stored as member variables
    private final SumHandler sumHandler;
    private final DivideHandler divideHandler;
    
    // GUI components are declared as member variables
    private final JPanel mainPanel;
    private final JLabel answerLabel;
    private final JButton sumButton;
    private final JButton divideButton;
    ...
    
    public CalculatorAppGUI(SumHandler sumHandler, DivideHandler divideHandler) {
        // Store handlers
        this.sumHandler = sumHandler;
        this.divideHandler = divideHandler;
        
        // Initialize components
        mainPanel = new JPanel(new SomeLayoutManager());
        
        answerLabel = new JLabel("Your answer will appear here");
        sumButton = new JButton("Sum");
        sumButton.addActionListener((ActionEvent e) -> {
            double answer = sumHandler.sum(/* Values from GUI */);
            answerLabel.setText(String.valueOf(answer));
        });
        ...
        
        mainPanel.add(answerLabel);
    }
    
    // Create a frame for the GUI and display it
    public void show() {
        JFrame frame = new JFrame();
        frame.setTitle("A Calculator");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        ...
        frame.getContentPane().add(mainPanel);
        frame.pack();
        frame.setVisible(true);
    }
    
    // Nested static handler interfaces
    @FunctionalInterface
    public static interface SumHandler {
        public double sum(double a, double b);
    }
    ...
}

本例中的处理程序非常简单,但在更大的应用程序中,它们将是更复杂的操作,例如JoinGameHandler 或诸如此类。在这里,应用程序功能和应用程序状态(是否连接到游戏?)将存储在CalculatorApp 类中,所有 GUI 代码将在CalculatorAppGUI 类中。这里的问题是,一个有很多按钮和很多功能的 GUI 会导致很多处理程序也不能很好地组织起来(你最终可能会有一百个处理程序来存储一个足够复杂的应用程序,这是只是不切实际)。

如果还不是很明显,我在 Java Swing 领域是 100% 自学的,还没有真正接触过这类东西的标准。感谢所有帮助。

【问题讨论】:

  • 检查了一种方法here
  • @trashgod 我看了你的链接,但这似乎并没有解决我的问题。我问的是程序结构,而不是 GUI 设计。
  • The issue here is that a GUI with a lot of buttons and a lot of features leads to a lot of handlers。这称为固有复杂性。您不能期望实现复杂应用程序的代码很简单。处理程序的方法很好。每个功能一个处理程序是功能内聚。 Craig Larman 定义了一个名为 Controller 的 GRASP 模式,它基于 system sequence diagrams 将表示层代码与域层代码分开。

标签: java swing user-interface design-patterns


【解决方案1】:

我问的是程序结构,而不是 GUI 设计。

没有一种设计模式可以解决所有的 GUI 设计问题,但许多知名的patterns 在 GUI 设计中反复出现。一些常见的启发式方法可能会有所帮助:

  • 使用 model-view-controlerobserver 模式作为loose coupling 的辅助;两种模式都经过检查here

  • 使用Action封装功能;在here 引用的几个例子中,KeyPadPanel 可能是最相关的。

  • 要管理复杂性,请使用nested classes 以简化原型设计;根据需要,可以将内部类提升为具有package-private 访问权限的单独类。

【讨论】:

  • 您对 MVC 模式的看法正是我所要寻找的;你也有一些很好的其他观点。不过,我很想看看其他人对这个话题有什么看法。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-08-23
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多