ActionListener在Java GUI App的控制器中是个好主意吗?

时间:2014-05-01 19:38:12

标签: java swing design-patterns model-view-controller

我没有尝试遵循MVC模式。在互联网上,我看到最着名的例子是计算器,例如here。我开始使用这种MVC模式的实现。但是现在我对控制器中的动作监听器有些疑虑,因为它们倾向于查看。

有很多变化与视图相关的主要原因 - 字体,颜色,边框等。此外还有仅仅修改视图的actionlisteners!因此,在控制器中实现这样的actionlistener要困难得多(与视图中的简单内部匿名类相比)。此外,它需要从控制器访问许多视图元素。

我有一个想法,在控制器和一些视图中保留一些actionlisteners,但它可能导致将来混乱。所以我想听别人的想法。

P.S。此问题与MVC pattern with many ActionListeners

不重复

4 个答案:

答案 0 :(得分:5)

MVC不是"严格的"图案。对原始模式有不同的解释,并且经常使用不同的衍生物,如MVPMVVM(即使人们说他们使用的是MVC)。

最重要的方面是将模型和视图分开。但有关它们如何连接的详细信息可能会有所不同,具体取决于应用案例。

MVC模式最常出现的问题是:"什么是控制器?"

答案:

  

"获得晋升的会计师#34;

根据我的个人经验,很少有理由拥有明确的" Controller"类。强制听众在一个" Controller"中积累和总结。班级有几个严重的缺点。为了在GUI组件和Model之间建立连接,您有两个选择:一个选项是允许访问视图组件以附加侦听器:

gui.getSomeButton().addActionListener(myActionListener);

我认为这是不可取的,因为它暴露了实施细节并阻碍了修改。另一种选择更好 - 即提供允许附加听众的方法:

gui.addActionListenerToSomeButton(myActionListener);

但我认为这是值得怀疑的,因为它仍然暴露了一个按钮的事实。例如,当你有一个JTextField输入一个数字,然后将其改为JSlider时,问题可能会变得更加明显:它将改变所需的监听器类型,尽管它应该只是一个问题的问题。

在Swing应用程序中,我认为可以将监听器视为"小控制器"。我认为让匿名监听器直接调用模型的方法是完全可行的(除非这些调用包含其他逻辑)。

话虽如此:我不会考虑你作为"良好" MVC的例子。首先,因为所选示例没有显示MVC的关键点:模型不包含状态,并且MVC中的模型通常是必须观察的事情(因此,当监听器附加)时,并不清楚。其次,由于上面提到的要点,如何建立GUI与模型之间的连接方式是有问题的。

我喜欢http://csis.pace.edu/~bergin/mvc/mvcgui.html的例子。部分也可以受到质疑(例如,使用通用的Observer / Observable类),但我认为它以令人信服的方式很好地展示了MVC的基本概念。


编辑:没有以ZIP格式下载此示例。但您可以将TemperatureModelTemperatureGUIFarenheitGUIMVCTempConvert复制并粘贴到IDE中。 (它假设存在CelsiusGUI。这个CelsiusGUI在网站上被省略,但在结构上与Farenheit GUI相同。对于第一个测试,它被实例化的行可能只是被注释掉了)。

添加侦听器的选项在此示例中由abstract TemperatureGUI类提供。 具体 FarenheitGUI类创建并附加实际侦听器。但这或多或少是一个实现细节。这里的关键点(也是针对原始问题)是由View以内部类甚至匿名类的形式创建。这些侦听器直接调用模型的方法。即,将温度设置为Farenheit(用于Farenheit GUI),或设置以摄氏度为单位的温度(对于Celsius GUI)。

仍有一定程度的自由。它不是一个完美的"或者"通用" MVC示例。但是,到目前为止,我发现的恕我直言,比其他大多数其他MVC示例更好,因为它很好地展示了重要的方面:

  1. 模型为Observable
  2. 视图为Observer
  3. "控制器" (也就是说,在这种情况下是Listeners)是由视图维护的匿名/内部类,以及模型的调用方法
  4. 在更复杂的常规设置中,不会使用Observable / Observer类。相反,人们可以为模型创建专用的侦听器和可能的事件。在这种情况下,这可能类似于TemperatureChangedListenerTemperatureChangedEvent。此处使用Observable / Observer类是为了简洁起见,因为它们已经是标准API的一部分。

    Agin,请注意可能存在更复杂的应用案例,其中在这个小例子中概述的MVC的想法必须略微扩展。特别是,当要执行的任务超越时,只需调用模型的方法。例如,当View包含多个输入字段时,在将数据传递给模型之前必须对其进行预处理或以其他方式验证。这些任务应该由匿名监听器完成。相反,这些任务可以概括在一个类中,然后可以被称为"控制器"。但是,实际的侦听器附加到GUI组件仍然只能由View完成。作为一个过于暗示的例子:这可能就像

    一样
    // In the view:
    someButton.addActionListener(new ActionListener()
    {
        @Override
        public void actionPerformed(ActionEvent e)
        {
            String s = someTextField.getText();
            Date d = someFormattedTextField.getDate();
            int i = someSlider.getValue();
    
            // The controller validates the given input, and
            // eventually calls some methods on the Model,
            // possibly using the given input values
            controller.process(s, i, d);
        }
    });
    

答案 1 :(得分:4)

我通常遵循以下设计模式,它对我很有效,因为它可以非常有效地分离模型视图和控制器

查看: 您的视图应包含所有jcomponents及其getter或setter,组件放置相关代码,布局相关代码。我从不向视图类中的任何组件添加任何侦听器,它只处理布局

<强>模型: 模型应该有占位符来保存您的视图数据,例如:如果您有JTable,模型可能包含一个将保存JTable数据的arraylist

控制器 =模型+视图(此处是您将模型与视图绑定的位置) 在这里添加所有的听众 绑定您的文本字段,组合框等 添加您的客户端业务逻辑 为您的按钮添加动作侦听器 这是您应该访问视图和模型的地方。

希望这有帮助。

答案 2 :(得分:3)

如果您遵循Controller GRASP pattern(假定您将界面和域图层分开),actionPerformed()代码将保留在界面层中。 concept is explained in the book,但为了清晰起见,我在本书的教师资源中加入了一个数字:

enter image description here

编辑:正如我在上面的评论中所说,如果您实施不同的视图(例如,在移动应用上,或添加语音识别),您希望将相同的系统操作发送到您的域层。对我来说,我喜欢视图层的概念“识别被转换为控制器命令的用户手势。”

答案 3 :(得分:1)

所有逻辑都应放在控制器中。然而,没有严格的线,一些非常简单的逻辑可以在视图中很好。将actionListeners放在控制器中,然后在GUI中放置按钮。这就是我通常做的事情。有时我很懒,让我的按钮实现actionListeners让他们自己听,在这种情况下我把按钮放在控制器包里。