Reputation: 73
I'm currently trying to develop a simple notepad in Java. However, I'm struggling with a correct handling of event listeners. I stick to the Single Responsibility Principle while programming. Unfortunately, the current way of handling the event listeners certainly breaks the principle.
Every time I create a new menu button, I have to implement the ActionListener and override the actionPerformed() method and pass it as an argument of the addActionListener() method. As a result, the entire code is becoming more and more complicated, and breaks the Single Responsibility Principle because the event listeners are not separated from the UI.
The problem becomes more serious when there are lots of components to handle. Handling 70 or 100 components in this way would make a mess in the whole code.
This is the code I'm talking about:
menuButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
JOptionPane.showMessageDialog(menuButton, "Message");
}
});
I've also tried to use inner classes for handling event listeners, but defining so many inner classes for every component is not a good idea when it comes to the CPU performance.
I've looked at the Observer pattern, but I've not managed to use it in the way not to break the Single Responsibility Principle.
How would you handle the event listeners in my application? Do you know how the event listeners are handled in professional and complex applications like MS Office or serious programs written in Java?
package notepad;
import javax.swing.*;
import java.awt.event.*;
import listeners.MenuButtonListener;
public class Buttons extends MenuButtonListener {
// The menu buttons
private JMenuItem openFile, saveFile, saveAs, close;
// Get the saveFile field
public JMenuItem getSaveFile() {
return saveFile;
}
/**
* Creates the items of the file menu
* @return JMenu
*/
public JMenu createFileMenuItems() {
JMenu file = new JMenu("File");
openFile = new JMenuItem("Open");
saveFile = new JMenuItem("Save", KeyEvent.CTRL_MASK);
saveAs = new JMenuItem("Save as");
close = new JMenuItem("Close");
// Add the listener to the button
saveFile.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
JOptionPane.showMessageDialog(saveFile, "Message");
}
});
saveFile.setMnemonic(KeyEvent.VK_T);
saveFile.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_T,
ActionEvent.CTRL_MASK));
file.add(openFile);
file.add(saveFile);
file.add(Separators.addSeparator());
file.add(saveAs);
file.add(close);
return file;
}
/**
* Creates the items of the edit menu
* @return JMenu
*/
public JMenu createEditMenuItems() {
JMenu edit = new JMenu("Edit");
JMenuItem cut = new JMenuItem("Cut");
JMenuItem copy = new JMenuItem("Copy");
JMenuItem paste = new JMenuItem("Paste");
JMenuItem find = new JMenuItem("Find");
edit.add(cut);
edit.add(copy);
edit.add(paste);
edit.add(Separators.addSeparator());
edit.add(find);
return edit;
}
/**
* Creates the items of the format menu
* @return JMenu
*/
public JMenu createFormatMenuItems() {
JMenu format = new JMenu("Format");
return format;
}
/**
* Creates the items of the help menu
* @return JMenu
*/
public JMenu createHelpMenuItems() {
JMenu help = new JMenu("Help");
return help;
}
/**
* Gets the specified menu item
* @return JMenuItem
*/
public JMenuItem getMenuItem(JMenuItem menuItem) {
return menuItem;
}
}
// This class should be used to handle the event listeners
package listeners;
import javax.swing.*;
import java.awt.event.*;
import notepad.Buttons;
public class MenuButtonListener {
// Create an inner class for every button
public class SaveButtonListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
Buttons buttons = new Buttons();
JMenuItem saveButton = buttons.getSaveFile();
if (e.getSource() == saveButton) {
JOptionPane.showMessageDialog(saveButton, "Save");
}
}
}
public SaveButtonListener returnSaveButton() {
return new SaveButtonListener();
}
}
Upvotes: 3
Views: 1305
Reputation: 33
Seems to me that you would benefit from using either MVC or MVP to structure your display code and your listener logic. Take a look at this other SO question for some background on both. Personally, I favor MVP, and though it is often associated with ASP.NET, MVP is really just a great pattern in any language including Java. I use it anytime I need to work with GUIs because it maintains SRP and maximizes testability. There is a little boilerplate overhead but I think it is worth it for the benefits MVP provides.
Upvotes: 0
Reputation: 20323
For such scenario you should use Mediator Pattern, It separates your UI completely from the controller(your listener), a very nice example.
Let me try to extend the example in Link to your notepad application.
You can have one command, controller for your 'File Menu' which takes care of all file operation like 'New', 'Open' e.t.c and another controller for your say 'Settings' menu. Now while creating the Menu 'File' you register it with mediator saying registerView(JComponent component, String component_type
), where component is JMenu, component_type is 'FILE', mediator can then look assign appropriate Controller to the view, this mapping of view to controller can be hard coded value (for notepad you can have it inside say a map) or can be read from configuration file (Say an application configuration xml where you define somthing like:
<application_config>
<view_controller_mapping>
<view_name>FILE</view_name>
<controller_name>FileController</controller_name><!--Optional-->
<controller_class>com.mycompany.notepad.view.controller.FileController</controller_class>
<!--Incase you want to give control to others to provide there own implementation -->
</view_controller_mapping>
</application_config>
Hope this helps.
Upvotes: 1
Reputation: 11543
What you want to do is move the functionality of what happens when your menu is selected to a separate method, and the only thing your action listener need to do now is to "glue" the selection of the menu to the method. This way you can use the same code from your menu, your button, or any other part of the program.
menuButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
showMessage();
}
});
private void showMessage() {
JOptionPane.showMessageDialog(frame, "Message");
}
Upvotes: 0