简体   繁体   English

如何在Java中设计ActionListener类?

[英]How to design an ActionListener class in Java?

NOTE: I'm learning about Clean Code , Design Patterns and Object Oriented Programming so please keep that in mind when answering. 注意:我正在学习“ 干净代码” ,“ 设计模式”和“ 面向对象的编程”,因此在回答时请记住这一点。

I've got a window with bunch of JButtons and a TextField on the top. 我有一个窗口,上面有一堆JButtons和一个TextField Here's the Window class in its separate file: 这是Window类的单独文件:

// Window.java
public class Window {
    JTextField textField;
    JButton button1;
    JButton button2;
    ...
}

Here's what I'd like: 这是我想要的:

When I press button1 I want the textField to display "1", when I press button2 to display "2", etc. So here's the ActionListener class in a separate file and this is sort of what I want it to do: 当我按下button1我希望textField显示为“ 1”,当我按下button2时显示为“ 2”, button2 。因此,这是单独文件中的ActionListener类,这是我想要它执行的操作:

//TextInputActionListener.java
public class TextInputActionListener implements ActionListener{
    public void actionPerformed(ActionEvent e) {
        if(e.getSource() == button1) {
            textField.setText("1");
        }
        else if (e.getSource() == button 2) {
            textField.setText("2");
        }
    }
}

Now obviously this wont work, so my question is how should I define this class? 现在显然这将行不通,所以我的问题是我应该如何定义此类?

  • Should I declare it as an inner class of Window ? 我应该将其声明为Window的内部类吗?
  • Should I not bother to create a separate class for it? 我是否应该为它创建一个单独的类? (I could just make the Window class implement ActionListener and that would solve the problem) (我可以使Window类实现ActionListener ,这将解决问题)

NOTE: As you can see the question is not how to do it but rather, how to do it in a way that supports Object Oriented Design. 注意:正如您所看到的,问题不在于如何执行,而是如何以支持面向对象设计的方式来执行。

With your current structure, the Window class can implement ActionListener because it is responsible for listening to events on its view. 使用当前结构, Window类可以实现ActionListener因为它负责侦听其视图上的事件。 An inner class would also be acceptable but could lead to more cluttered code. 内部类也可以接受,但可能导致代码混乱。 You should be watchful of excessive separation of concerns. 您应该注意过多的关注点分离。 You should only really separate your code in terms of the Model, the View, and the Controller. 您应该只在模型,视图和控制器方面真正分开代码。

Definitely check out the MVC design pattern. 绝对签出MVC设计模式。

Since listener classes often need access to fields of GUI classes (like your Window ) using inner classes for listeners is a good idea. 由于侦听器类经常需要使用内部类作为侦听器,因此需要访问GUI类的字段(例如Window )。

Of course it is not forbidden that Window implements ActionListener but you then expose implementation details in the public API and you should think if you want that. 当然,并非禁止Window实现ActionListener但是您随后在公共API中公开了实现细节,您应该考虑是否要这样做。

Note that lambdas and method handles of Java 8 give you even more possibilities to write listener code: 请注意,Java 8的lambda和方法句柄为您提供了更多编写侦听器代码的可能性:

class Window {
    JTextField textField;
    JButton button1;
    JButton button2;


    Window()
    {
        button1.addActionListener(event -> textField.setText("1"));
        ...
    }
}

Here is my take on this problem : 这是我对这个问题的看法:

  • Should ActionListener implementation be declared as an inner class of Window? 是否应将ActionListener实现声明为Window的内部类? - I would not. - 我不会。 This is because inner classes are used when there is a piece of functionality that is closely associated with the state of the containing class. 这是因为当存在与包含类的状态紧密相关的功能时,会使用内部类。 For eg an Iterator<E> implementation can be written as an inner class of Collection<E> implementation. 例如,可以将Iterator<E>实现编写为Collection<E>实现的内部类。 In this case the Iterator implementation has access to the private data members of the Collection implementation. 在这种情况下, Iterator实现可以访问Collection实现的私有数据成员。 Another example is the Builder pattern .Inner classes have access to the private members of the parent class and hence should be used with care. 另一个示例是Builder模式。内部类可以访问父类的私有成员,因此应谨慎使用。
  • Should you create a separate class for it - Yes, and you should declare it in a separate file at minimum required access levels. 是否应该为它创建一个单独的类-是的,并且应该在一个最低要求的访问级别的单独文件中声明它。 You would not want to make the Window class implement ActionListener - simply because this would make the Window class responsible to handle events for all it's containing controls - violation of the separation of concerns (Single Responsibility Principle). 您不希望使Window类实现ActionListener -仅仅因为这会使Window类负责处理所有包含控件的事件-违反关注点分离(单一责任原则)。 So imagine the code you would be writing - it would be fraught with a long list of if conditions or a switch case to identify the source of the event. 因此,想象一下您将要编写的代码-它将充满一堆漫长的if条件或用于确定事件源的开关案例。 This obviously implies that if you add a new control to the window class you increase the length of the if or switch block. 显然,这意味着如果向窗口类添加新控件,则会增加ifswitch块的长度。 Declaring the action listener in a separate class allows for separations of concerns and also helps testability. 在单独的类中声明动作侦听器可以分离关注点,还有助于提高可测试性。

Hope this helps 希望这可以帮助

First of all I'd like to thank everybody who answered the question. 首先,我要感谢回答问题的每个人。 Without you I would've not figured it out. 没有你,我不会弄清楚。 Each one of you gave me a piece of the puzzle. 你们每个人都给我一个难题。

My initial problems were: 我最初的问题是:

  1. TextInputListener class needs access to buttonX . TextInputListener类需要访问buttonX
  2. TextInputListener class needs access to textField . TextInputListener类需要访问textField

In the TextInputListener class I figured that it doesn't really need access to the buttons, it only has to know if e.getSource() equals button . 在TextInputListener类中,我认为它实际上不需要访问按钮,只需要知道e.getSource()等于button So I created methods in the Window class that takes an ActionEvent as a parameter (like e ), compares it to buttonX and returns returns the answer. 因此,我在Window类中创建了将ActionEvent作为参数的方法(例如e ),将其与buttonX进行比较并返回返回答案。

//Window.java
...
boolean isButton0(ActionEvent e) {
    return e.getSource() == buttons[0];
}
boolean isButton1(ActionEvent e) {
    return e.getSource() == buttons[1];
}
...

PROBLEM 1 SOLVED: 解决的问题1:

So now I'm one step closer. 所以现在我离我们更近了一步。 I can determine whether button1 , button2 .. was pressed, without declaring the buttons public or without returning it by a "getter" method like getButton1() . 我可以确定是否已按下button1button2 ..,而无需将按钮声明为公共按钮,也无需通过类似getButton1()的“ getter”方法将其返回。

// TextInputListener.java
public class TextInputListener implements ActionListener {
    Window window;
    @Override
    public void actionPerformed(ActionEvent e) {
        if (window.isButton0(e)) {
            //textField.setText("0")
        } else if (window.isButton1(e)) {
            //textField.setText("1")
        }
        ...
    }
}

( TextInputListener.java and Window.java are in the same package so I was able to declare the methods package-private. ) TextInputListener.java和Window.java在同一程序包中,因此我可以将方法声明为package-private。

PROBLEM 2 SOLVED: 解决的问题2:

Once again TextInputListener didn't really need textField (as a variable) it just needed to set its text. 同样, TextInputListener并不需要真正的textField (作为变量),只需要设置其文本即可。 So I created another package-private method setOutputText(String text) for setting the text. 因此,我创建了另一个程序包专用方法setOutputText(String text)来设置文本。 Here's the code: 这是代码:

// Window.java
public class Window {
    TextField textField;
    JButton button1;
    JButton button2;
    ...
    void setText(String text) {
        textField.setText(text);
    }
}

// TextInputListener.java
public class TextInputListener implements ActionListener {
    Window window;
    @Override
    public void actionPerformed(ActionEvent e) {
        if (window.isButton0(e)) {
            window.setText("0");
        } else if (window.isButton1(e)) {
            window.setText("1");
        }
        ...
    }
}

PUTTING EVERYTHING TOGETHER: 将所有内容放在一起:

The only thing that's left now is to let each instance of the class to know about each other. 现在剩下的唯一事情就是让该类的每个实例相互了解。 In TextInputListener class I added the following code: TextInputListener类中,我添加了以下代码:

public void listenTo(Window window) {
        this.window = window;
    }

In the Window class I had to add the ActionListener to each button, so I added the following code: 在Window类中,我必须将ActionListener添加到每个按钮,因此添加了以下代码:

public void setActionListener(ActionListener l) {
        for (int i = 0; i < buttons.length; i++) {
            buttons[i].addActionListener(l);
        }
    }

That's basically it! 基本上就是这样! In main set everything up and it's working. 在主要设置中,一切正常,并且正在运行。 Here's the (almost) full final code: 这是(几乎)完整的最终代码:

// MyApp.java
public class MyApp {
    public static void main(String[] args) {
        Window myWindow = new Window();
        TextInputListener myTextInputListener = new TextInputListener();

        myWindow.setActionListener(myTextInputListener);
        myTextInputListener.listenTo(myWindow);
    }
}

// Window.java
public class Window {
    TextField textField;
    JButton button1;
    JButton button2;
    ...
    void setText(String text) {
        textField.setText(text);
    }
    public void setActionListener(ActionListener l) {
        for (int i = 0; i < buttons.length; i++) {
            buttons[i].addActionListener(l);
        }
    }
}

// TextInputListener.java
public class TextInputListener implements ActionListener {
    Window window;

    public void listenTo(Window window) {
        this.window = window;
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        if (window.isButton0(e)) {
            window.setText("0");
        } else if (window.isButton1(e)) {
            window.setText("1");
        }
        ...
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM