【问题标题】:Java, Large GUI classes, with many ActionListeners; best way to organize the listeners?Java,大型 GUI 类,带有许多 ActionListener;组织听众的最佳方式?
【发布时间】:2013-08-25 12:48:30
【问题描述】:

我已经开发 Java 程序 1½ 年了。我目前正在做一个暑期项目,其中涉及到相当大的图形用户界面。

我的 GUI 由几个选项卡式窗格组成。每个窗格都有自己的类。每个窗格都有几个 jButton。

现在,我已经到了一个点,在我的选项卡式窗格类中有这么多匿名内部类(用于 ActionListeners),我确信一定有更好的方法;如果不是为了效率,也是为了可维护性——它变得一团糟。

我的问题是:当每个班级都有很多听众时,有没有更好的方法来组织听众?我考虑过将监听器聚集在相关的类中——比如下面的示例代码:

public class SomeListeners implements ActionListener{

    @Override
    public void actionPerformed(ActionEvent e){
        String command = e.getActionCommand();
        switch(command){
            case "This button":
                doThis();
                break;
            case "That button":
                doThat();
                break;                          
        }
    }     
}

或者有没有更好的方法?

提前致谢:)

【问题讨论】:

  • 我喜欢@duffymo 关于使用依赖注入的建议,但不确定他为什么删除它。它不必是 Spring,因为 Guice 可以正常工作。它可以很好地配合treeno 的建议以及Tim Herold 的建议(对他们来说都是1+)。一切都是为了尽可能松耦合和收紧内聚。
  • 我复制了 duffymo 的答案并发布为社区 wiki 答案,因为它应该是一个完整的答案。

标签: java swing actionlistener


【解决方案1】:

我建议使用 javax.swing.AbstractAction 类型的单个类

例子:

Action leftAction = new LeftAction(); //LeftAction code is shown later
...
button = new JButton(leftAction)
...
menuItem = new JMenuItem(leftAction);

leftAction = new LeftAction("Go left", anIcon,
         "This is the left button.",
         new Integer(KeyEvent.VK_L));

...

class LeftAction extends AbstractAction {
    public LeftAction(String text, ImageIcon icon,
                      String desc, Integer mnemonic) {
        super(text, icon);
        putValue(SHORT_DESCRIPTION, desc);
        putValue(MNEMONIC_KEY, mnemonic);
    }
    public void actionPerformed(ActionEvent e) {
        displayResult("Action for first button/menu item", e);
    }
}

【讨论】:

    【解决方案2】:

    在某些时候,匿名内部类会从“方便”变成一团糟。你已经通过了这一点。

    和 Tim 一样,我建议您使用 AbstractAction。然而,为了扩展,创建您自己的抽象子类,它可以从配置文件中读取其名称、图标、描述(用于工具提示)等。这样您就可以将代码国际化,而其他人(营销)可以轻松地将按钮从“保存”更改为“SuperSomethingTM”或其他。并且图形艺术家可以轻松地修改图标。为实际实现扩展这个类。

    AbstractAction 的另一个优点是它们可以被禁用。因此,如果没有要保存的更改,您可以轻松禁用保存菜单、保存按钮和工具栏中的保存图标。

    有多种方法可以存储和读取此配置信息。一个是 ResourceBundle。

    (在我的 Nook 上输入,所以这很短,其他人可以随意扩展)

    【讨论】:

      【解决方案3】:

      您可以尝试以下方法:

      使用真实类而不是匿名类。每个 ListenerClass 只实现一个用例/功能。类名应该描述用例。然后,您可以将这些类组织在一个或多个包中,按照适合包中侦听器实现的用例的类别对它们进行聚类。

      这意味着,您创建了一个抽象层次结构,它像树结构一样组织功能。

      如果某天之后有人必须维护 Listener,他/她可以通过首先查找适合 UseCase 的包然后查找 UseCase 本身来找到 Listener。由于您将拥有比 Classes 更少的包,因此找到 Listener 会更容易和更快。


      另一种思考方式:如果您在一个选项卡上有这么多事件,您会得到 在代码中组织它们时遇到问题,如何在选项卡上直观地组织它们?你能以符合人体工程学的方式为用户处理吗?也许解决方案可能是将功能拆分到多个选项卡上?但由于我不知道你的 UI,我不能说太多

      【讨论】:

      • 我发现你的评论很有教育意义。谢谢好心的先生:)
      • 是的,好主意。我仍然有我的答案中的子类 AbstractAction,但这是组织它们的好方法。
      • @Daniel:很高兴 :-)
      • @user949300:是的,我同意这可以结合在一起。
      【解决方案4】:

      JSR 296 旨在为您正在做的事情以及这里提到的许多其他事情(i18n、Actions 等)提供一个框架。最活跃的实现是Better Swing Application Framework,或BSAF。可以在here 找到一篇关于如何使用任何 JSR 296 实现的好文章。

      【讨论】:

      • 谢谢!感谢您的回复!
      • BSAF 似乎没有得到积极维护。我确实在我自己的项目中使用了它,该项目在 JSR 296 看起来将成为 Java 的标准部分时开始开发。如果我开始一个新项目,我可能不会使用 BSAF。
      【解决方案5】:

      Per duffymo:使用依赖注入并传入监听器。这将使您有机会随意重用和更改它们。

      Per me (hovercraft):不一定是 Spring,因为 Guice 可以正常工作。它可以很好地配合treeno 的建议以及Tim Herold 的建议(对他们来说都是1+)。一切都是为了尽可能松耦合和收紧内聚。

      【讨论】:

      • 我认为通过在 XML 中“编程”来连接复杂的侦听器代码是一个非常糟糕的主意。但在我听到更多回应之前不会投反对票。但是,如果某些操作根据动态状态启用或更改,比如您正在编辑的文档,则很难想象使用 Spring 等会有好的结果。
      • 曾几何时,我也在考虑这个想法(使用 Spring 框架开发 Swing 应用程序),但我放弃了认为这是一种矫枉过正的做法。或者是吗?我希望看到这个问题得到改善,我想让我错了。 ;)
      • 我仍然不认为这是矫枉过正。侦听器实际上是控制器的一部分——它们是由视图事件触发的操作。我更愿意将视图构建为纯显示并将智能保持在它之外。 HFoE 关于 Guice 的评论是正确的——任何 DI 引擎都可以。如果您认为 Spring 太重,请选择其他东西,但要认识到 Spring 不是“全有或全无”。您可以只使用您感兴趣的功能,而保留其他功能。
      • -1 由于没有人解释这些监听器是如何被动态禁用的,我不得不投反对票。
      • 首先,移除监听器不会禁用按钮。 (我相当确定?)。其次,您不希望不断地添加和删除数十个小部件的侦听器。你怎么知道哪些?谈论复杂性和内存泄漏! Action 有一个 enabled 属性,并为您触发事件 - 按预期使用它。听众没有。此外,由于侦听器是使用 DI 添加的,例如Spring 或 Guice,而您建议通过 Java 代码删除它们,这只是可怕的重复 DRY。
      【解决方案6】:

      类似这样的代码

      public class SomeListeners implements ActionListener{
      
          @Override
          public void actionPerformed(ActionEvent e){
              String command = e.getActionCommand();
              switch(command){
                  case "This button":
                      doThis();
                      break;
                  case "That button":
                      doThat();
                      break;                          
              }
          }     
      }
      

      【讨论】:

      • 我不明白你的第二个子弹。当doThis()(在某处连接到按钮和菜单)需要禁用时,因为尚未加载文件,或者用户没有以管理员身份登录,或者网络连接断开,只需你会如何在这个系统中做到这一点?
      • 将actionperformed中的事件分离到逻辑块中,超过50pct事件组合用于两个或三个JButtons或JMenuItems,一个void可以被几个ActionPerformed重用,然后我看到了这个地方用于 Swing 动作中的 setEnabled
      • 我个人不关心开关/外壳。我会将操作留给客户。
      • 仍然很困惑 - 什么是 SwingAction 以及它是如何连接的?如果您的意思是 javax.swing.Action,这就是我一直在建议的,如果是这样,为什么还要使用单独的侦听器呢?我同意@duffymo 关于开关/案例的看法。
      • 我从不建议 switch - case,EventHandler 的逻辑非常不同,hmmm 没有严格的逻辑,一切皆有可能,所有监听器之上的父级(不包括键和鼠标) ,顺便说一句,您是否尝试过我的代码示例,仍然感到困惑 - 什么是 SwingAction == 高度可能的抽象,可访问,可设置的可重载,忘记这一点,在我的回答中忽略,重要的是 EventHandler 而已
      猜你喜欢
      • 2011-09-20
      • 1970-01-01
      • 1970-01-01
      • 2011-02-01
      • 1970-01-01
      • 1970-01-01
      • 2020-12-23
      • 2011-01-22
      • 2010-09-08
      相关资源
      最近更新 更多