【问题标题】:Separating GUI from logic in Java将 GUI 与 Java 中的逻辑分离
【发布时间】:2015-06-16 14:32:35
【问题描述】:

我浏览了大量关于如何将 GUI 与 Java 中的其他程序逻辑分开的文章和(imo 太复杂)示例,老实说,我仍然不知道。

有人可以告诉我如何在这个简单的示例中做到这一点,只需一个按钮吗?

假设 Window 类中有一个按钮:

JButton firstButton = new JButton("My first button");      
btnCreateProject.setBounds(100, 100, 80, 30);
frame.getContentPane().add(firstButton);

假设这个按钮将调用 Employee 类的构造函数。所以,我可以这样做:

JButton firstButton = new JButton("My first button");
firstButton.addActionListener(new ActionListener() {
   public void actionPerformed(ActionEvent e) {
      .....constructor calling, and rest of the code....
   }
});
btnCreateProject.setBounds(100, 100, 80, 30);
frame.getContentPane().add(firstButton);

但这正是我不想要的。我希望我的 Window 类只是纯 GUI,带有按钮、单选框和其他东西,以及它的属性。而已。我想将所有侦听器放在另一个类中,比如说 Controller,然后从这个类中调用所有需要的方法。

谁能给我一个例子来说明如何使用这个简单的按钮来做到这一点?非常感谢。

【问题讨论】:

  • 不要将监听器实现为匿名内部类。完全按照你说的去做。喜欢stackoverflow.com/a/4985539/2055998
  • 看起来你正在创建一个 Swing 应用程序,所以this thread 应该提供一些关于正确分离关注点的想法。
  • @TinoArts 只是想知道您是否使用过 angularJS ?
  • 另外,分层架构的一个重要组成部分是dependency injection。基本上这意味着您不应该在控制器/GUI 类中实例化模型或服务。

标签: java user-interface model-view-controller controller


【解决方案1】:

您是在区分程度,而不是种类。您的 GUI 代码不能完全与程序逻辑分开,否则它将是现代艺术的静态作品。

各种 AWT/Swing 侦听器类确实将 GUI 与逻辑分开,形成完全独立的类。但是,您不需要将侦听器实现为匿名内部类;也许将它们实现为普通的顶级类对您来说会更好。或者,您可能会发现它适合您通过实现Action 接口的类来对程序行为进行建模。然后您可以通过控件的setAction() 方法将这些行为分配给控件(按钮、菜单项)。

然而,无论如何,设置 GUI 的代码必须以某种方式了解 GUI 组件和需要连接到它们的逻辑,否则它无法完成它的工作。

【讨论】:

  • 基本上 Java 不太支持 MVC 设计模式?
  • 不要将 Swing 与 Java 混合使用
  • @KickButtowski,我看不出我的回答如何支持这样的结论。但是,如果您想挑毛病,那么您似乎必须特别选择 Swing,而不是一般的 Java。
  • @KickButtowski 是的,但也有 JavaFX,据说它是继任者,更好地支持 MVC。
  • @KickButtowski,而不是 Swing,JavaFX 可能更适合 AngularJS。 JavaFX 提供声明性标记(通过 FXML)和绑定功能。如果与 afterburner.fx 配对,那么您将拥有一个带有依赖注入的 MVP 样式模型。所以,有点类似于 Angular,但仍然不同。 Swing 和 JavaFX 都是 Oracle Java Runtime 发行版的一部分。
【解决方案2】:

这是一个新类的构造函数:

new ActionListener() {
   public void actionPerformed(ActionEvent e) {
      .....constructor calling, and rest of the code....
   }
}

你可以创建一个这样的类:

公共类 ActionListenerTest 实现 ActionListener { ... }

然后做这样的事情:

firstButton.addActionListener(new ActionListenerTest());

【讨论】:

    【解决方案3】:

    我的项目也有同样的问题,但最后我决定使用我在 android 编程中学到的概念。以下是它的工作原理:

    我向我的对象添加一个标识(对于按钮、菜单等,我使用 setActionCommand 方法),因此我可以对不同组件的相同操作使用相同的标识符。

    我所有的对象都调用一个控制器,它们的 getActionCommand 作为 cmd 和对象本身。在控制器中,我控制 cmd 并从中调用正确的方法。

    控制 GUI 和程序的其余部分要容易得多。

    这对我来说就像魅力一样。

    import java.awt.Dimension;
    import java.awt.FlowLayout;
    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
    import javax.swing.JButton;
    import javax.swing.JDialog;
    
    /**
     *
     * @author Pasban
     */
    public class ActionListenerTest {
    
        public static void main(String[] args) {
            JButton b1 = new JButton("My first button");
            JButton b2 = new JButton("My first button");
    
            b1.setActionCommand("BTN_1");
            b2.setActionCommand("BTN_2");
    
            //put this in another class
            ActionListener controller = new ActionListener() {
    
                @Override
                public void actionPerformed(ActionEvent e) {
                    String cmd = e.getActionCommand().toUpperCase().trim();
                    System.out.println(cmd);
    
                    //System.out.println(e.getSource()); // cast to anything you have in your mind about the caller
    
                    if (cmd.equals("BTN_1")) {
                        System.out.println("BUTTON 1 is clicked");
                    } else if (cmd.equals("BTN_2")) {
                        System.out.println("BUTTON 2 is clicked");
                    }
                }
            };
    
            b1.addActionListener(controller);
            b2.addActionListener(controller);
    
            JDialog frame = new JDialog();
            frame.setSize(new Dimension(300, 300));
            frame.setLocationRelativeTo(null);
            frame.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
    
    
            frame.getContentPane().setLayout(new FlowLayout());
            frame.getContentPane().add(b1);
            frame.getContentPane().add(b2);
    
            frame.setVisible(true);
    
        }
    }
    

    【讨论】:

      【解决方案4】:

      你总是可以实现类似的东西:

      Controller.handleActionEvent(ActionEvent e);
      

      并将您的按钮点击委托给该按钮。如果你不想让你的控制器知道 Swing,你总是可以创建一些接口并将 Swing ActionEvent 包装在你创建的某种事件中。

      或者只是在 UI 类中实现一个观察者(监听器)模式(或者你可以有一个位于中间的“注册表”类)。单击按钮时,UI 会将事件委托给所有已注册的侦听器(实现您定义的某些接口)。因此,控制器(客户端)只会告诉 UI“单击按钮 X 时通知我”,而 UI 只会委托给所有相关方。

      因此,用户界面不需要明确知道要调用谁。但控制器必须知道他想听谁的。

      【讨论】:

        【解决方案5】:

        您应该做的是在适当的地方编写一个方法来访问数据模型(假设您有一个)并在那里完成工作,然后只需单击按钮调用该方法。

        firstButton.addActionListener(e -> logicClass.addEmployeeToFirm());
        

        或者您可以编写一个自定义侦听器来调用/执行该逻辑(实现 ActionListener 的类)。

        【讨论】:

        • 更好的是:SwingUtilites.invokeLater(new Runnable() { logicClass.addEmployeeToFirm(); })
        • 这仍将在 EDT 线程上完成。如果是繁重的工作,则根本不应该在 EDT 上完成,但我认为这不是这个问题的重点。
        【解决方案6】:

        在你的控制器中,创建一个方法

        Actionget[Operation]Handler(); //填写[Operation]将你的按钮功能应该做什么

        它返回一个实现正确功能的内部类。

        然后,在你的 GUI 代码中,做

        JButton b = ...;
        b.setAction(controller.get[Operation]Handler());
        

        【讨论】:

        • 你能扩展一下吗?
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-07-03
        • 2013-12-15
        • 1970-01-01
        相关资源
        最近更新 更多