使用 JFrame 的图形应用程序设计模式

Design pattern for graphical application using JFrame

我一直在 Java 中通过扩展 JFrame class 编写我的图形应用程序,如下所示:

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 很好地放入它自己的可以创建和显示的小 class 中,将应用程序的功能与 GUI 分开,但缺点是必须管理许多烦人的处理程序.处理程序是一种向 GUI 提供应用程序功能的方法。此结构也更容易转换为其他 JWindow,例如 JDialog,因为不必更改整个 class 层次结构。

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 class 中,所有 GUI 代码将存储在 CalculatorAppGUI class.这里的问题是,带有很多按钮和很多功能的 GUI 会导致很多处理程序,这些处理程序也不能很好地组织(对于足够复杂的应用程序,您最终可能会存储一百个处理程序,这是只是不切实际)。

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

I am asking about program structure, not GUI design.

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

  • 使用 model-view-controllerobserver 模式作为 loose coupling; both patterns are examined here 的辅助。

  • 使用Action to encapsulate functionality; among the several examples cited hereKeyPadPanel可能是最相关的。

  • 要管理复杂性,请使用 nested classes for ease in prototyping; as the need arises, inner classes can be promoted to separate classes having package-private 访问权限。