最佳实现actionPerformed的方法是什么?

4
在Java中,我们有两种实现actionPerformed的方法:
  1. 每个类一个actionPerformed方法:使用if-else检查事件源;
  2. 使用匿名内部类,在创建对象的地方实现actionPerformed方法;
对于选项1,很容易跟踪actionPerformed代码所在的位置,因为每个类只有一个方法。我们可以轻松定位该方法,然后查找所需的代码。但是,如果有许多操作侦听器,则可能有太多的if-else子句。
对于选项2,不容易找到actionPerformed代码所在的位置,因为它分散在整个类中。如果您有许多操作侦听器,则更难跟踪actionPerformed实例。
我个人更喜欢选项1,因为它比较容易维护代码,即使性能可能不如选项2高效。
每种方法的优缺点是什么?在选择如何实现actionPerformed时还应考虑什么?您更喜欢哪种方式?
编辑:根据A. Lee的建议,还有第三个选项。它是选项1和选项2的组合:使用匿名内部类,但将它们全部放在类中的一个位置。我认为这是个好主意。

1
也许类似的主题(今天)http://stackoverflow.com/questions/5856083/swing-component-listening-to-itself-vs-inner-classes - mKorbel
如果你想一直停留在过程式编码的阶段,那就选择1吧;但对于面向对象编程来说,这是绝对不行的。 - kleopatra
为什么选项2是面向对象的,而选项1不是?是因为选项2将所有代码放在一起,可以轻松地复制到另一个地方吗?还是因为我们将动作与对象分开了? - 5YrsLaterDBA
请仔细阅读 - 我并没有说2是面向对象的 ;-) - kleopatra
4个回答

3
我认为在actionPerformed方法中使用长长的if-else链有几个不好的地方 - 每次都需要进行不必要的检查以确定适当的接收器代码是哪个ActionEvent,随着代码的增长,它变得越来越难以维护。
如果你想将所有的操作处理程序放在一个地方,可以使用一个initializeActionListeners方法将你的ActionListeners绑定到你的控件上。或者在初始化它们时将它们绑定在更靠近小部件/控件本身的事件处理程序上,例如:
// not thread-safe
private JLabel getSomeLabel() {
    if (someLabel == null) {
        someLabel = new JLabel("Some label, huh");
        someLabel.addActionListener(...)
    }
    return someLabel;
}

初始化动作侦听器将会像这样: - 5YrsLaterDBA
initializeActionListeners会是这样的吗? private void initializeActionListener() { someLabel.addActionListener(new ActionListener(){ public void actionPerformed(ActionEvent e){...}}); someBtn.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e){}}); ... } - 5YrsLaterDBA
你是否担心20或40个不必要的检查会影响性能?我认为一个长的条件链并不难维护。相反!你只需要在那里调用单独的方法,而不是把所有的工作都放在那里。 - user unknown
是的,@5YrsLaterDBA。 @user unknown - 在分析器告诉我需要关注性能影响之前,我不会在意20-40个不必要的检查,但如果您能编写更清晰的代码,这将更容易维护并避免不必要的工作,为什么不呢?当然,权衡是一个if-else检查与一个匿名类的额外分配。 - A Lee
不要编造API - 没有JLabel.addActionPerformed,事实上,在Swing中没有这样的方法。 - kleopatra
感谢 @kleopatra 捕捉到那个错别字,应该是 s/addActionPerformed/addActionListener/。 - A Lee

1

错误的选择:问题不是匿名类与长长的if-else结构(你可以在前者中轻松地拥有后者)。问题是是否要有一个语义上强大的类(无论是匿名的还是非匿名的),它只做一件事情,是唯一做这件事情的人,并且完全做到了。毕竟,我们处于面向对象的领域;-)

所以:绝对不能使用任何if-then-else结构。


你编写 actionPerformed 方法的方式是什么?使用选项 2 吗? - 5YrsLaterDBA
好的,我明白了,你将使用Actions而不是ActionEvent和ActionListener。 - 5YrsLaterDBA

0
在大多数情况下,没有太大区别可以建议其中之一。
我会说:如果情况类似,请将它们放在一起,但只调用适当的方法:
    public void actionPerformed (ActionEvent e)
    {
            String cmd = e.getActionCommand ();
            if (cmd.equals (cmdNextPage))        nextPage ();
            else if (cmd.equals (cmdLastPage))   lastPage ();
            else if (cmd.equals (cmdOk))         ok ();
            // ... and so on

这样,您可以轻松地调用方法,独立于事件,也许是从测试框架或撤销/重放中。常见的模式显然可见。

在非常罕见的情况下,您可能会对单个事件和源进行注销。然后,每个ActionEvent一个监听器似乎更合适。

从纯面向对象的角度来看,我认为每个按钮(例如)一个监听器更直接。从不成熟的优化愿望来看,我不喜欢20或40个ActionListeners,但在我的理性时刻,我不相信40个ActionListeners有可衡量的影响。但它们会混乱您的文件系统、IDE浏览器,所以我想最终有一个理性的原因,保持监听器数量较少。

一个常见的监听器在按钮工厂中很容易使用:

    private void addButton (JPanel jp, String cmd, String ttt)
    {
            JButton jb = new JButton (cmd);
            jb.setToolTipText (ttt);
            jp.add (jb);
            jb.addActionListener (this);
            buttonlist.add (jb);
    }

    /**   @return a Panel with Action-Buttons.     */
    private JPanel createControlPanel ()
    {
            JPanel m = new JPanel ();
            m.setLayout (new BoxLayout (m, BoxLayout.Y_AXIS));

            JPanel jpc = new JPanel ();
            jpc.setLayout (new FlowLayout ());
            buttonlist = new ArrayList <JButton> ();

            addButton (jpc, cmdLastPage, "last page");
            addButton (jpc, cmdNextPage, "next page");
            addButton (jpc, cmdOk, "save");
            addButton (jpc, cmdEsc, "exit");

非常容易和清晰,可以很明确地知道在哪里添加新按钮以及如何添加。我发现它非常易于维护。


我正在使用与你的按钮工厂非常相似的方法。节省了很多代码,而且易于维护。 - 5YrsLaterDBA
不要使用那些过时的过程式遗物——即使Swing内部代码这样做了也不行……顺便说一下,不要使用ActionListeners,而是使用Actions。 - kleopatra
你有支持你观点的论据吗,还是只有强烈的个人看法? - user unknown

0

我使用第三种选择,编写了自己的管理器,通过注释直接将方法绑定到JButtons。

我将其作为开源项目提供,您可以在https://github.com/MarkyVasconcelos/Towel/wiki/ActionManager上查看。

优点是您不需要实现任何ActionListener,也不需要匿名内部类。

所有操作都在您的类的方法中。


似乎很难理解,我需要赶上那些新的Java特性。 - 5YrsLaterDBA
有点混淆,因为我也使用了@Bindables注释。但你需要学习的唯一一件事就是@Action,它非常容易使用。 - Marcos Vasconcelos
但是你不会失去类型安全吗?ActionListener或/和匿名内部类有什么问题吗? - user unknown
你会失去一些类型安全,但编码会更加容易。匿名内部类并没有什么问题,但是你写的代码量通常不适用于这种情况,你会将逻辑写入该类中,使代码针对该情况进行特定指定。而我的方法是,在方法中编写代码,易于以其他方式调用并将逻辑解耦以供其他用途。 - Marcos Vasconcelos

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接