HomeDigital EditionSys-Con RadioSearch Java Cd
Advanced Java AWT Book Reviews/Excerpts Client Server Corba Editorials Embedded Java Enterprise Java IDE's Industry Watch Integration Interviews Java Applet Java & Databases Java & Web Services Java Fundamentals Java Native Interface Java Servlets Java Beans J2ME Libraries .NET Object Orientation Observations/IMHO Product Reviews Scalability & Performance Security Server Side Source Code Straight Talking Swing Threads Using Java with others Wireless XML

Turning Components into Domain GUI Objects (DGO)
Improving your design

When creating user interfaces for a data entry application (as opposed to one where the user is directly manipulating graphics, such as a network diagram designer), a typical scenario is to create some containers, instantiate their layout managers, and add some components such as JLabels, JTextFields, or JButtons. When the application needs to gather the information entered by the user, it accesses the components directly. Figure 1 shows a screen with a table of information (a book inventory) and a search area that allows users to find one of the books by various criteria specified in the fields.

Figure 1

To find a book with a given word in its title or description, the user enters the query criteria and presses the "Search" button. After the query has executed, the matching items in the table are then highlighted. Listing 1 shows a typical example of how the code can be written where BookSearchPanel.java creates the GUI components, then adds an action listener to the button that, when pressed, calls a doSearch() method that uses the current component's values to create a search string. (Listings 1-4 can be found below.) This article shows the limitations of this coding technique and how to improve it by using Domain GUI Objects.

Issues with a Typical Solution
In Listing 1 there is very tight coupling between the Swing components and how the search properties are determined. To find out if the Title should be searched, the search code directly invokes isSelected() on the checkbox.

boolean isSearchInTitle = m_searchInTitleCheckBox.isSelected();

A problem could arise if the user interface changed - the columns to be searched would be specified by selecting items in a JList or highlighting columns in the JTable. The search code would then have to be modified to use the different components, when all that changed is how the GUI is being presented to the user. The explicit knowledge the search logic needs of the presentation components can be improved upon by decoupling the two.

Another reason why Listing 1 is so poor is that it's not the responsibility of the search code to determine what the query options are. That logic should be elsewhere, and only the information on what the options are (not how they're represented) should be required by the search code. By separating the two, it's possible to add features such as allowing the user to save searches to replay later. Also, what about testing the search code (because I know all of you unit test your code, right?). The "quick and dirty," tightly coupled implementation is looking less attractive now.

Loosening the Coupling
There are a couple of choices for improving the design, but I'll focus on making the "bunch o' components" into a black box that represents a piece of the application domain. I call this a Domain GUI Object or DGO. The goal is to turn these components into a self-contained object that has a queryable interface for its state. That way, instead of invoking isSelected() on the "Title" checkbox directly, the code calls isSearchInTitle() on the DGO. Listing 2 shows how the BookSearchDgo extends JPanel and has a constructor that accepts an ActionListener that is called back when the search is activated. The query details are retrieved through get methods.

public boolean isSearchInTitle() {
return m_searchInTitleCheckBox.isSelected();
}

The presentation logic is encapsulated in BookSearchDgo, which hides its implementation from the code that performs the actual search. This search code can be put in a separate class (BookSearch.java, see Listing 3) that registers itself with the BookSearchDgo (using an ActionListener passed into the DGO's constructor), and when the "Search" button is clicked it queries the BookSearchDgo through its public interface and proceeds on its merry way.

public class BookSearch implements ActionListener {
private BookSearchDgo m_bookSearchDgo;
public BookSearch() {
m_bookSearchDgo = new BookSearchDgo( this );
}
public void actionPerformed( ActionEvent e ) {
doSearch();
}
private void doSearch() {
boolean isSearchInTitle = m_bookSearchDgo.isSearchInTitle();

Having the BookSearch class instantiate the BookSearchDgo isn't a great way to decouple logic, but the discussion of what creates what and when will have to wait for a future column.

If the way the GUI appears is altered (see Figure 2), such as the Match Case radio buttons becoming a single checkbox, the interface remains the same and the search code is unaltered. This is the advantage of separating the GUI from the search or business logic.

Figure 2

Loosening the Coupling Even More
Now we feel great: the user interface can change and the search code will remain the same. Uh oh, there's that change of requirements again since the users want the search to be updated in real time. As they type the search text and check the checkboxes, they want the search to be updated immediately without clicking on a "Search" button. Now what?

The solution, shown in Listing 4, is to loosen the coupling at the point where the search code assumed that it was waiting for the "Search" JButton to be clicked (and therefore the ActionListener to be called). Instead of waiting for a button click, the search code performs the search when a more "functional" or "semantic" event occurs. For this an UpdateSearchListener interface is created with a single callback updateSearch() callback method.

interface UpdateSearchListener {
void updateSearch( BookSearchDgo bookSearchDgo );
}

The BookSearchDgo accepts an UpdateSearchListener in its constructor

public BookSearchDgo(UpdateSearchListener updateSearchListener)

and the BookSearch class implements UpdateSearchListener so it's no longer reliant on an ActionPerformed event:

public class BookSearch implements UpdateSearchListener {

Now the search code doesn't know, or for that matter care, how or why the updateSearch() method was invoked. It could have been that the "Search" button was clicked, one of the checkboxes checked, or just the passage of a specific interval of time. All that the search logic knows is that it needs to update the query, which it still does by getting the search parameters from the DGO that are conveniently passed directly via the updateSearch() method.

Lessons Learned
Tight coupling is bad; loose coupling (up to a point) is good. Tightly coupled code is hard to test, hard to reuse, and easier to break. We've seen two ways to loosen coupling:

  1. Raise the level of abstraction: Instead of the search code accessing the state of checkboxes, it invokes methods that return state; instead of using ActionEvents, we use UpdateSearchEvents.
  2. Reallocate responsibilities: Let the DGO collect the information from the GUI and then pass the information to the search code in a compact "Transfer Object" that has only the search query data.
The Next Steps
In future columns, I'll show how to handle continuing changes in the requirements that affect the GUI: the items that match the search query need to be highlighted; only items that match the search query are shown (filtering); how to switch between two search modes - easy and advanced; and the need to support searching over a large number of items without affecting performance (or at least perceived performance).

About the Author
Ted M. Young is a staff software engineer at eBay, Inc., leading the development of internal desktop tools using Java and Swing technologies. He also cofounded LearningPatterns.com in 1996, the first Java training company in the U.S. [email protected]

"Turning Components into Domain GUI Objects (DGO)"
Vol. 9, Issue 2, p. 55

Source Code for this Article zip file ~5.92 KB

All Rights Reserved
Copyright ©  2004 SYS-CON Media, Inc.
  E-mail: [email protected]

Java and Java-based marks are trademarks or registered trademarks of Sun Microsystems, Inc. in the United States and other countries. SYS-CON Publications, Inc. is independent of Sun Microsystems, Inc.