Definition:  Commands are executed when the user presses one of the buttons on the button panel.  The buttons are for such functions as “Save”, “Cancel”, “Find”, etc.  This action is executed on the panel which implements the Actionable interface.  Not all buttons are present on every screen, but those which are present, must be able to notify the Actionable panel to perform the action.  In the case of “Save”, for example, each of the input elements on the Actionable panel must save their input to the appropriate place.  The “Cancel” button may do something like ask for confirmation, then close the Window, JFrame, or JDialog.

Issue:  Since there are a large number of input screens, and a corresponding number of button panels, many with the same or similar buttons and commands to be executed, I needed a way to simplify this.  I decided to use the Command Pattern to solve this problem.

Requirements:  Here are the requirements to accomplish this task:

Solution:  I first had to create a JButton which holds an CommandButtonsEnum instance.  This was straight forward, and it has a constructor which takes both the CommandButtonsEnum instance and the display text as arguments.  That’s about the only difference from a JButton.  It’s called (unsurprisingly) a “CommandButton”.
My next solution was to create an enum of the buttons/actions needed.  I called this enum “CommandButtonsEnum” and it looked like this:

	public enum CommandButtonsEnum {
		SAVE { public void execute(Actionable a) { a.save(); } }, 
		EXIT { public void execute(Actionable a) { a.exit(); } }, 
		CANCEL { public void execute(Actionable a) { a.cancel(); } }, 
		FIND { public void execute(Actionable a) { a.find(); } };
		
		CommandButtonJButton getButton(ActionListener al) {
			CommandButton button = new CommandButton(this, ...);
			button.addActionListener(al);
			...
			return button;
		}
		
	}  //  end enum CommandButtonsEnum

There are far more commands than the four shown here, but I just want to get the process across, and not go into every detail.  The constructor for CommandButtonsEnum, which is not shown for simplicity, retrieves the internationalized name and saves it, and also computes the size of the largest (widest) button, then applies that size to all future buttons.  The getButton() method creates an actual button, with the appropriate ActionListener, text, and size.  This is the actual button which is placed on the button panel.  The ActionListener is usually the button panel.  Here is a short look at the button panel:
	public class ButtonPanel2 implements ActionListener {
		Actionable a;
		
		public ButtonPanel2(Actionable a, CommandButtonsEnum[] b) {
			this.a = a;
			// the buttons are laid out on the panel
		}
		
		public void actionPerformed(ActionEvent ae) {
			Object source = ae.getSource();
			//  CHANGES HERE
			if (source instanceOf CommandButton) {
				((CommandButton)source).execute(a);
			}
		}
	}  //  end class ButtonPanel

The key here is that the ButtonPanel is the listener for the buttons it contains, then calls the execute() method for the appropriate command based on the CommandButton, and the Actionable object provided when the ButtonPanel is created.  The source is checked to verify it is a valid CommandButton.  Here is how the input panel looks, in short:
	public class InputPanel extends ActionablePanel {
		CommandButtonsEnum[] buttons = { CommandButtonsEnum.SAVE, CommandButtonsEnum.EXIT};
		ButtonPanel bp = new ButtonPanel(this, buttons);
		JTextField input = new JTextField("data here");
		...
		@Override public void save() {
			//  save the input field data
		}
		@Override public void exit() {
			//  exit
		}
	}  //  end class InputPanel

The InputPanel extends the ActionablePanel which is a plain JPanel which implements Actionable by using a “stub” method for each of the commands.  The “stub” method does nothing, so only the needed commands, in this case SAVE and EXIT, need to be overridden on this InputPanel to do anything.

So here is what happens when the user presses (clicks on) the “Save” button.


This is slightly better than the original version, but I'm not so sure it is really worth the effort to change all of the existing code which uses these classes.  At the moment I’ll leave everything as it is.