Façade clears complexity

Develop dialog boxes and Swing apps faster

1 2 Page 2
Page 2 of 2

Example 3. Use a Swing application façade

public class LibraryViewer extends JFrame {
   ...
   private static final String
      FILEMENU_TITLE = "app.frame.menus.file.title",
      FILEMENU_NEW   = "app.frame.menus.file.new",
      FILEMENU_EXIT  = "app.frame.menus.file.exit",
      EDITMENU_TITLE = "app.frame.menus.edit.title",
      EDITMENU_CUT   = "app.frame.menus.edit.cut",
      EDITMENU_COPY  = "app.frame.menus.edit.copy";
   ...
   public static void main(String args[]) {
      ApplicationSupport.launch(new LibraryViewer(), 
                   ApplicationSupport.getResource("app.frame.title"),
                   windowX, windowY, windowW, windowH);
   }
   public void clearStatusArea() {
      ApplicationSupport.showStatus("");
   }
   public void selectDVD(boolean valueIsAdjusting) {
      ...
      ApplicationSupport.showStatus((String)table.getValueAt(table.getSelectedRow(), 0));
   }
   private JMenu makeEditMenu() {
      return ApplicationSupport.addMenu(this, EDITMENU_TITLE,
               new String[] { EDITMENU_CUT, EDITMENU_COPY });
   }
   private JMenu makeFileMenu() {
      JMenu fileMenu = ApplicationSupport.addMenu(this, FILEMENU_TITLE, 
                              new String[] {FILEMENU_NEW, FILEMENU_EXIT});
      fileMenu.getItem(1).addActionListener(new ActionListener() {
         public void actionPerformed(ActionEvent e) {
            System.exit(1);
         }
      });
   }
   public void categoryChanged(JTabbedPane pane) {
      int selectedIndex = pane.getSelectedIndex();
      if(selectedIndex >= 0)
         ApplicationSupport.showStatus(pane.getTitleAt(selectedIndex));
   }
   private void makeCategoryTabs() {
      categoryPane.addTab(ApplicationSupport.getResource("app.books.tab"), booksPanel);
      categoryPane.addTab(ApplicationSupport.getResource("app.dvds.tab"),  dvdPanel);
      categoryPane.addTab(ApplicationSupport.getResource("app.vhs.tab"),   vhsPanel);
   }
   private void initializeTable() {
      String[] columnNames = new String[] {
         ApplicationSupport.getResource("dvd.title.column.heading"),
         ApplicationSupport.getResource("dvd.rating.column.heading"),
         ApplicationSupport.getResource("dvd.languages.column.heading"),
         ApplicationSupport.getResource("dvd.length.column.heading"),
      };
      DefaultTableModel model = new LibraryTableModel();
      for(int i=0; i < columnNames.length; ++i)
         model.addColumn(columnNames[i]);
      table.setModel(model);
   }
   private JComponent makeToolbar() {
      JToolBar toolbar = new JToolBar(); 
      JButton newButton = new JButton(new ImageIcon(ApplicationSupport.getResource(
                                                     TOOLBAR_NEW_ICON)));
      JButton openButton = new JButton(new ImageIcon(ApplicationSupport.getResource(
                                                   TOOLBAR_OPEN_ICON)));
      JButton saveButton = new JButton(new ImageIcon(ApplicationSupport.getResource(
                                                   TOOLBAR_SAVE_ICON)));
      JButton cutButton = new JButton(new ImageIcon(ApplicationSupport.getResource(
                                                   TOOLBAR_CUT_ICON)));
      JButton copyButton = new JButton(new ImageIcon(ApplicationSupport.getResource(
                                                   TOOLBAR_COPY_ICON)));
      JButton pasteButton = new JButton(new ImageIcon(ApplicationSupport.getResource(
                                                   TOOLBAR_PASTE_ICON)));
      JButton redoButton = new JButton(new ImageIcon(ApplicationSupport.getResource(
                                                   TOOLBAR_REDO_ICON)));
      ...
      return toolbar;
   }

The preceding code uses ApplicationSupport.launch() to position and size the application's window, set the window's title, and set up a window listener that closes the application when the close box in the window is activated. The application uses the façade's getResource() method to internationalize text displayed to the user based on key/value entries in a resource file. The application also uses the façade to display information in a status panel (created by the façade) and the façade's addMenu() method to easily create a menu.

Finally, the preceding application uses the ApplicationSupport and JOptionPane façades to create a localized dialog box, shown in Figure 7.

Figure 7. An error dialog. Click on thumbnail to view full-size image.

Example 4 shows the code that displays Figure 7's dialog. Thanks to façades, two lines of code are all it takes to display a localized dialog.

Example 4. Use façades to create a localized dialog

   private void showErrorDialog(String missingFilename) {
      String msg = ApplicationSupport.formatMessage("error.missing.file",
                                                    new String[] {missingFilename});
      JOptionPane.showMessageDialog(LibraryViewer.this, msg,
               ApplicationSupport.getResource("error.missing.file.dialog.title"),
               JOptionPane.ERROR_MESSAGE);
   }

The ApplicationSupport.formatMessage() method formats a pattern (the method's first argument) given a set of arguments (the method's second argument) to format a localized message with the given filename. Because you probably don't format messages everyday, encapsulating that behavior in a façade is a great timesaver.

Example 5 lists the ApplicationSupport class.

Example 5. The ApplicationSupport façade

public final class ApplicationSupport {
   static private final String PREFS_BUNDLE_BASENAME = "prefs";
   static private final String BUNDLE_BASENAME = "app", PREFERRED_LOCALE_KEY = "locale";
   static private final JPanel statusArea = new JPanel();
   static private final JLabel status = new JLabel();
   static private ResourceBundle preferences, resources;
   static private Locale locale;
   static {
      try {
         preferences = ResourceBundle.getBundle(PREFS_BUNDLE_BASENAME);
         locale = new Locale(preferences.getString(PREFERRED_LOCALE_KEY));
      }
      catch(java.util.MissingResourceException ex) {
         System.err.println("ERROR: cannot find preferences properties file " + 
                            BUNDLE_BASENAME);
      }
      try {
         resources = ResourceBundle.getBundle(BUNDLE_BASENAME, locale);
      }
      catch(java.util.MissingResourceException ex) {
         System.err.println("ERROR: cannot find properties file for " + BUNDLE_BASENAME);
      }
   };
   // Disallow direct instantiation
   private ApplicationSupport() {}
   
   public static void launch(final JFrame f, String title,
                       final int x, final int y, 
                       final int w, int h) {
      f.setTitle(title);
      f.setBounds(x,y,w,h);
      f.setVisible(true);
      f.setResizable(true);
      status.setHorizontalAlignment(JLabel.LEFT);
      statusArea.setBorder(BorderFactory.createEtchedBorder());
      statusArea.setLayout(new FlowLayout(FlowLayout.LEFT,0,0));
      statusArea.add(status);
      f.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
      f.addWindowListener(new WindowAdapter() {
         public void windowClosed(WindowEvent e) {
            System.exit(0);
         }
      });
   }
   public static Locale getLocale() {
      return locale;
   }
   public static JMenu addMenu(final JFrame f, String titleKey,
                               String[] itemKeys) {
      JMenuBar mb = f.getJMenuBar();
      if(mb == null) {
         mb = new JMenuBar();
         f.setJMenuBar(mb);
      }
      JMenu menu = new JMenu(ApplicationSupport.getResource(titleKey));
      for(int i=0; i < itemKeys.length; ++i) {
         menu.add(new JMenuItem(ApplicationSupport.getResource(itemKeys[i])));
      }
      mb.add(menu);
      return menu;
   }
   public static JPanel getStatusArea() {
      return statusArea;
   }
   public static void showStatus(String s) {
      status.setText(s);
   }
   public static String getResource(String key) {
      return (resources == null) ? null : resources.getString(key);
   }
   public static String formatMessage(String patternKey, String[] params) {
      String pattern = ApplicationSupport.getResource(patternKey);
      MessageFormat fmt = new MessageFormat(pattern);
      return fmt.format(params);
   }
}

All ApplicationSupport methods are static, which is common for a façade because often those methods are unrelated. The ApplicationSupport class can launch a Swing application, localize text from a resource bundle, display information in a status panel, and create localized menus. The ApplicationSource class makes it significantly easier to implement Swing applications.

Fortunately for façades

Powerful APIs, which are plentiful in Java, are wonderful when you need a power tool, but for most basic uses, façades provide a simpler alternative that greatly eases application development. In this article, I've discussed a Swing built-in façade—the JOptionPane class—and a custom façade—ApplicationSupport—that make it much easier to create Swing applications.

David Geary is the author of Core JSTL Mastering the JSP Standard Tag Library (Prentice Hall, 2002; ISBN: 0131001531), Advanced JavaServer Pages (Prentice Hall, 2001; ISBN: 0130307041), and the Graphic Java series (Prentice Hall). David has been developing object-oriented software with numerous object-oriented languages for 18 years. Since the GOF Design Patterns book was published in 1994, David has been an active proponent of design patterns, and has used and implemented design patterns in Smalltalk, C++, and Java. In 1997, David began working full-time as an author and occasional speaker and consultant. David is a member of the expert groups defining the JSP Standard Tag Library and JavaServer Faces, and is a contributor to the Apache Struts JSP framework.

Learn more about this topic

This story, "Façade clears complexity" was originally published by JavaWorld.

Copyright © 2003 IDG Communications, Inc.

1 2 Page 2
Page 2 of 2