Skip to main content

A design pattern for Menubars

Posted in

There is this thing about Java GUI programming, that has been irking me for quite a long time. Namely the way, a lot of people tend to write ActionListener routines, which often involves the use of inner classes. I don't really know why inner classes are so popular. Personally, I never liked them, always considered them to be defective by design and generally more helpful in obfuscating code then producing clarity.

I still remember the first Java book I read several years ago. While it was not a bad book in general, it suggested the use of inner classes for handling buttonevents. I was instantly disgusted, when I saw the given example, which looked somehow like the following snippet and to me appeared to be completely out of place style wise.

JButton button = new JButton("Args!");
button.addActionListener(new ActionListener() {
  public void actionPerformed(ActionEvent e) {
  doMyAction();  // Suppose to live somewhere in the outer class!!!
  }
}
);

I won't even comment on the (non) aesthetics of code like this beyond stating, that such nonsense is hard to read. What I would want to point out is the fact, that this brilliant solution does it's best to make sure that neither classloader nor garbage collector get bored too much. It's also a neat way to bloat your sourcecode, too! Just think about having a dozen buttons like let's say in a menubar and each requiring a piece of clumsy code like this.

So, after having looked at one of the worst implementation ideas possible, how can a menubar be implemented in a clean and efficient way? The following example shows the approach, I usually choose, when building a menubar (though I rarely subclass JMenuBar). It follows a distinct pattern and is very scalable without bloating the code. It does not supported nested menus, but these should be avoided for ergonomic reasons anyway. Binding of acceleratorkeys is not shown, but adding support for them is pretty straightforward.

import javax.swing.*;
import java.awt.event.*;

public class MyBar extends JMenuBar implements ActionListener {

  // Define the menuitems. Don't worry about organizing, yet.
  private JMenuItem open = new JMenuItem("Open");
  private JMenuItem save = new JMenuItem("Load");
  private JMenuItem exit = new JMenuItem("Exit");
  private JMenuItem about = new JMenuItem("About");
  private JMenuItem license = new JMenuItem("License");

  // Constructor
  public MyBar() {
    // The following two arrays have to be properly alligned.  
    JMenu[] menus = {
      new JMenu("File"),
      new JMenu("Edit"),
      new JMenu("Help")
    };
   
    // As long as there are no sub menus (which should be avoided for
    // ergonomic reasons anyway), a two dimensional array provides
    // a convinient mapping. 
    AbstractButton[][] but = {
      {open,save,exit}, // These will go into the "File" menu  
      {},               // The "Edit" menu stays empty for now
      {about,license}   // These will go into the "Help" menu
    };
   
    // Because of the mapping into arrays, building the menustructure and
    // wiring up the actionlisteners becomes as easy as iterating over them.
    // This method scales nicely, if more menu(items) have to be added later.
    for (int x=0;x<but.length;x++) {
      add(menus[x]);
      // For aesthetic reasons
      if (x==menus.length-2) add(Box.createHorizontalGlue());
      for (int y=0;y<but[x].length;y++) {
        menus[x].add(but[x][y]);
        but[x][y].addActionListener(this);
      }
    } 
  }   
    
  public void actionPerformed(ActionEvent e) {
    Object src = e.getSource();
    System.err.println("Handling: "+src);
   
    // Yes, don't use equals() for comparisson. It's safe and sane to use
    // "==" here, as it is what Object.equals() would do anyway, so save the
    // method call for the sake of performance.
    if (src == open) handleOpen();
    if (src == save) handleSave();
    if (src == exit) handleExit();
    // ...
  }
  
  // And here are the actual handlerXX routines for doing the dirty work.
  // It pays to have a naming convention like "handle"+"Name of the
  // corresponding menuitem" here.
  private void handleOpen() {}
  private void handleSave() {}
  private void handleExit() { System.exit(0); }
 

  public static void main(String[] args) {
    JFrame frame = new JFrame();
    frame.setJMenuBar(new MyBar());
    frame.getContentPane().add(new JTextArea(10,20));
    frame.pack();
    frame.setVisible(true);
  }

As can be seen in the code above, there are actually two design patterns at work here, which nicely dovetail with each other. The first one is used in the constructor and takes care of registering the items. The second one is a naming convention, which makes it easy to associate a handler routine with the object in question. Expanding the menu therefore becomes a three step process:

  1. Define a new JMenuItem as a global, private attribute.
  2. Write a handler method with the name of the method being the name of the associated attribute, prefixed by the string "handler".
  3. Register the item in the constructor and the handler in the actionPerformed method.

Doing it this way instead of using inner classes makes sure the entire code remains easily manageable and saves about 0.5kb per menuitem in terms of compiled code size. The classloader will only have to fetch one class and the garbage collector will have less objects to monitor.