【问题标题】:ActionListener style - Good or bad [closed]ActionListener 风格 - 好或坏 [关闭]
【发布时间】:2012-10-30 08:07:03
【问题描述】:

我有一个简单的 GUI,其中包含:

  • 一个按钮。
  • 两个单选按钮

现在我想听听这些按钮中的每一个。我所做的是这样的:

public class TestApp implements ActionListener {

    private JFrame frame;
    private JButton btn;
    private JRadioButton rdb1;
    private JRadioButton rdb2; 

    public static void main(String[] args) { /*....*/ }

    private void initialize() {
       //Each time I add a button, I add it to the listener:
       btn = new JButton("Button");
       btn.addActionListener(this);
       //..
       rdb1 = new JRadioButton("Value1");
       rdb1.addActionListener(this);
       //And so on...
    }

    //The ActionEvents  
    public void actionPerformed(ActionEvent e) {
       if(e.getSource()==btn)
       //...
       if(e.getSource()==rdb1)
       //...        
    }
}

现在我想知道这种风格是好还是坏?

【问题讨论】:

  • 什么都没有,我想知道它是坏还是好。
  • 通过让你的主要公共类实现 ActionListener 来暴露你的 ActionListener 是不好的风格。最好切换到内部类或匿名类
  • 我只是想知道...你为什么要把你的生活复杂化?简单地说,使用匿名类。
  • 我对匿名类不是很熟悉,我会阅读它,我会知道它有什么优点:) 谢谢
  • +1 一个好问题。我见过一些机构要求学生只使用 1 个监听器来处理多个完全不同性质的按钮。我很惊讶学校会这样做,因为我总是认为这种事件处理方式是一种非常糟糕的习惯。

标签: java swing coding-style inner-classes anonymous-class


【解决方案1】:

你可以考虑另外两个想法:

  1. 为每个 UI 元素提供自己的监听器;这样他们就完全独立了。
  2. 将侦听器注入到您的 Swing UI 中,而不是调用 new。你让用户有机会改变他们认为合适的行为。让你的 Swing UI 做它应该做的事情:显示结果。而已。我认为监听器是控制器逻辑的一部分。

【讨论】:

    【解决方案2】:

    这在一定程度上取决于你想在actionPerformed方法中做什么。如果没有其他类可能想要调用这个方法,那么我可能会想通过创建一个内部类来缩小actionPerformed 方法的范围,例如:-

    public class TestApp {
    
        private JFrame frame;
        private JButton btn;
        private JRadioButton rdb1;
        private JRadioButton rdb2; 
    
        private class CombinedActionListener implements ActionListener {
             public void actionPerformed(ActionEvent e) {
                 if(e.getSource()==btn)
                 //...
                 if(e.getSource()==rdb1)
                 //...        
                 }
        }
    
        public static void main(String[] args) { /*....*/ }
    
        private void initialize() {
           ActionListener listener = new CombinedActionListener()
    
           //Each time I add a button, I add it to the listener:
           btn = new JButton("Button");
           btn.addActionListener(listener);
           //..
           rdb1 = new JRadioButton("Value1");
           rdb1.addActionListener(listener);
           //And so on...
        }
    }
    

    您甚至可以通过将按钮实例传递给构造函数来使侦听器类成为静态内部类或顶级类 - 这将使侦听器类更易于测试。

    正如我上面所说,这在很大程度上取决于 i) 是否有其他人可能调用此方法,以及 ii) 方法内部逻辑的复杂性。

    【讨论】:

    • 但是根据其他答案,每个按钮的匿名类可能是最好的方法。
    • @Strawberry 这是上下文相关的。在所呈现的情况下,它可能是,但匿名的使用并不总是合适的
    • @MadProgrammer 你是对的。我通常只在最简单的情况下使用匿名类(我在这里假设)。如果有任何复杂或协作的逻辑,我通常会分解为静态内部类或包私有顶级类。我的重点是能够测试逻辑。
    【解决方案3】:

    除非监听器是一个很长的方法,否则我个人更喜欢匿名类模式:

            final JButton btn = new JButton("Button");
            final JRadioButton rdb1 = new JRadioButton("Value1");
            final ActionListener listener = new ActionListener() {
                @Override
                public void actionPerformed(final ActionEvent e) {
                    if (e.getSource() == btn) {
                        //...
                    } else if (e.getSource() == rdb1) {
                        //...        
                    }
                }
            };
            btn.addActionListener(listener);
            rdb1.addActionListener(listener);
    

    甚至更好:

        btn.addActionListener(new ActionListener (){
             public void actionPerformed(ActionEvent e) {      
                 // btn handling code
                 }
        });
        rdb1.addActionListener(new ActionListener (){
             public void actionPerformed(ActionEvent e) {      
                 // rdb1 handling code
                 }
        });
    

    您使用的模式允许其他类将 TestApp 类设置为其他类的侦听器 - 除非有意这样做,否则这不是一个好习惯。

    【讨论】:

    • @thdayofcondor No. +1 Now :)
    • 如果我的听众很长,我应该像上面展示的那样保持它吗?还是有匿名课更好?
    • @Maroun85 我认为您应该始终使用匿名类。
    • 方法中的长监听器会使方法冗长且混乱,但非匿名类会污染您的命名空间
    • @Strawberry 是的,并且使用 if -else 来确定生成事件的控件很笨拙,但不幸的是,这是许多教程中发现的模式
    【解决方案4】:

    更面向对象的方式是创建一个匿名类来实现每个监听器。

    只创建一个开启事件源组件的监听器可读性不强,而且当监听器数量增加时,容易出错。您很容易忘记处理 switch 块(或 if-else 块链)中所有可能的事件源,这将导致 运行时异常静默错误行为(在这种情况下不会发生任何事情)。

    为每个组件添加单独的侦听器将使您编译时检查您没有忘记处理所有这些。

    public class TestApp {
    
        // you can initialize fields inline to make thing shorter and safer
        private JButton btn = new JButton("Button");
        private JRadioButton rdb1 = new JRadioButton("Value1");
    
        private void initialize() {
            btn.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    // something
                }
            });
            rdb1.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    // something else
                }
            });
        }
    }
    

    由于匿名类语法非常冗长,可以通过将监听器移到私有字段中来缩短初始化方法的代码。

    public class TestApp {
    
        private JButton btn = new JButton("Button");
        private JRadioButton rdb1 = new JRadioButton("Value1");
    
        private void initialize() {
            btn.addActionListener(btnListener);
            rdb1.addActionListener(rb1Listener);
        }
    
        private final ActionListener btnListener = new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                // something
            }
        };
    
        private final ActionListener rb1Listener = new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                // something else
            }
        };
    }
    

    【讨论】:

    • “最面向对象”我会讨论这个问题。您的回答不允许继承已实现的类。一种更面向对象的方法是使用一个单独的专用类来实现 ActionListener 或 Action API - 恕我直言 - 请不要,我不是在质疑你的答案,这对于给定的用例是正确的
    • 好吧,也许不是“最面向对象”,但肯定比一种打开源的方法“更面向对象”。在不同条件下实现不同行为时,OOP 与过程方法通常在使用多态与条件和开关方面有所不同。
    【解决方案5】:

    很大程度上取决于动作侦听器尝试执行的操作的复杂性。如果您想要小的、一次性的操作,那么匿名类就适合了。

    使用这种监听器风格的主要好处是它可以准确地隔离动作正在做什么以及它是为谁做的。当侦听器包含超过 10 行或更多行时,就会出现缺点,因为它开始变得难以阅读并知道侦听器实际结束的位置。

    在这种情况下,像内部类这样的东西可能更合适。它具有匿名类的好处(与使用它的类绑定),但更易于阅读。

    如果您想要可重用的操作(想想诸如打开、新建、保存之类的事情),那么您最好使用 Action API,它提供自配置性以及自包含的操作侦听器

    恕我直言

    【讨论】:

    • "如果您想要可重用的操作.." 请注意,每个 Action 可能同时用于按钮(或主窗口中的保存按钮,以及在几个对话框中)& 菜单项。就我的口味而言,这就解决了问题。
    • @AndrewThompson 和键绑定 ;)
    • “键绑定” 哦是的.. :)
    猜你喜欢
    • 1970-01-01
    • 2021-12-17
    • 2014-07-04
    • 1970-01-01
    • 2012-05-19
    • 2010-10-20
    • 1970-01-01
    • 2017-01-31
    • 2012-04-01
    相关资源
    最近更新 更多