2016-01-15 19 views
0

注:我了解清洁守则设计模式面向对象编程所以请回答时记住这一点。如何在Java中设计ActionListener类?

我有一堆窗口,上面有一堆JButtons和一个TextField。下面是其单独的文件窗口类:

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

这是我想什么:

当我按下button1我想textField显示“1”,当我按下button2显示“ 2" 等,因此,这里是在一个单独的文件中的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"); 
     } 
    } 
} 

现在很明显这不会工作,所以我的问题是我应该如何定义这个类?

  • 我被宣布作为一个内部类的Window
  • 难道我不打算为它创建一个单独的类吗?(我可以只作Window类实现ActionListener这将解决这个问题)

注:正如你所看到的问题不是如何去做,而是,如何做到这一点的方式支持面向对象的设计。

+0

#1)扩展Action而不是实现ActionListener。 #2)你可以在Window类中定义动作,或者传入将要操作的参数。这两种方法都很好。 –

回答

0

首先我要感谢大家谁回答的问题。没有你我就不知道了。你们每个人都给了我一个难题。

我最初的问题是:

  1. TextInputListener类需要访问buttonX
  2. TextInputListener类需要访问textField

在TextInputListener类我计算过,它并不真正需要访问的按钮,它只有知道如果e.getSource()等于button。因此,我在Window类中创建了一个方法,其中ActionEvent作为参数(如e),将其与buttonX进行比较,然后返回答案。

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

问题1解决:

所以我现在更近了一步。我可以确定是否按下了button1,button2 ..,但没有声明按钮是公开的,也没有通过像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和Window.java是在相同的封装所以我能够申报方法包私人。)

问题2解决:

一旦再次TextInputListener并不真的需要textField(作为一个变量)它只需要设置其文本。所以我创建了另一个包私钥方法setOutputText(String text)来设置文本。下面的代码:

// 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"); 
     } 
     ... 
    } 
} 

把一切TOGETHER:

现在剩下的工作就是让类的每个实例来了解彼此的唯一的事情。在TextInputListener类添加以下代码:

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

在Window类我不得不把ActionListener添加到每个按钮,所以我加了如下代码:

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

这基本上!主要设置一切,它的工作。下面是(几乎)完整的最终代码:

// 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"); 
     } 
     ... 
    } 
} 
1

利用您当前的结构,Window类可以实现ActionListener,因为它负责监听其视图上的事件。内部类也是可以接受的,但可能会导致更多混乱的代码。你应该注意过度分离的担忧。您应该只根据模型,视图和控制器真正分离您的代码。

绝对检查出MVC设计模式。

+0

尚未研究过MVC模式,但它非常有用。感谢您为我指出:) –

1

由于侦听器类经常需要访问GUI类的字段(比如你的Window),所以对于侦听器使用内部类是个好主意。

当然,不禁止Window实现ActionListener,但是您在公开API中公开了实现细节,并且您应该考虑是否需要这样做。

注意的Java 8的Lambda表达式和方法把手让您有更多的可能性写监听器代码:

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


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

这是我对这个问题:

  • 应该ActionListener执行被声明为一个Window的内部类? - 我不会。这是因为当有一个与包含类的状态紧密相关的功能块时使用内部类。对于例如一个Iterator<E>实现可以被编写为Collection<E>实现的内部类。在这种情况下,Iterator实现可以访问Collection实现的私有数据成员。另一个例子是Builder pattern。内部类可以访问父类的私有成员,因此应谨慎使用。
  • 是否应该为它创建一个单独的类 - 是的,并且您应该以最低要求的访问级别将其声明在单独的文件中。你不会想让Window类实现ActionListener - 只是因为这会使Window类负责处理所有的事件它包含控件 - 违反关注点分离(单一责任原则)。所以想象一下你将要写的代码 - 它将充满一长串的if条件或一个开关情况来识别事件的来源。这显然意味着如果将新控件添加到窗口类中,则会增加ifswitch块的长度。在单独的类中声明动作侦听器允许分离关注点,并且还有助于可测试性。

希望这有助于

+0

谢谢你的有用答案,但如果我应该在一个单独的文件中声明actionlistener类,如何访问按钮并修改文本字段?如果你可以编辑你的答案并给我提供一些很棒的例子代码! :) –

+0

为您的TextInputActionListener)类创建一个构造函数,并将所需的参数传递给它:'button.addActionListener(new TextInputActionListener(textField));' – FredK