ONJava.com -- The Independent Source for Enterprise Java
oreilly.comSafari Books Online.Conferences.

advertisement

AddThis Social Bookmark Button

BlackMamba: A Swing Case Study

by Ashwin Jayaprakash
03/10/2004

Developing a UI-based program is an excruciating experience. It takes years to get it right. The tried-and-tested approach is to develop it in layers (Remember "Onion-boy" from Shrek?). Today, everybody talks about MVCs and Singletons. So, I'll assume that you already know what MVC is. Implementing MVC looks straightforward until you sit down to write the code. Over the years I have refined my approach, which I'll explain momentarily. Note that there is no single way to do it right. I chose my method because it's simple and I believe in the "Principle of Parsimony", also known as "Occam's Razor".

My latest hobby project is a semi-automatic email previewer and spam filter that I call BlackMamba. It is a Swing-based application, fairly small and limited in its functionality. But it does its job well. It uses a library called Mail4Me to communicate with the POP3 server. So it is a "networked" application and that adds another layer of complexity. It also uses a lot of text files to load and store data: Mail accounts, Spammer list, Address book, etc. So you see that even designing such a small application requires a lot of aspects to be considered.

In this article we'll discuss how to develop a desktop application using many of the architectural principles in the proverbial Book of OOAD. BlackMamba, shown in Figure 1, will be our case study. We will also list some of the common pitfalls that one encounters when developing such an application in Java Swing and learn how to overcome them.

Click for larger view
Figure 1. BlackMamba in action. (You can click on the screenshot to open a full-size view.)

Layers

If you are a visual kind of person who prefers to use the mouse rather than the keyboard, you will find writing Java Swing code an arduous task.

Since we are following the MVC approach, we are told that the model can be developed independently and the control and view can later be sewed onto it. It has been my experience that form almost never follows function. It is the other way round. If your model has not already been developed, then consider creating a prototype on paper or a whiteboard to get a feel of the flow in the application and how the data has to be partitioned across screens. If your client has specified any User Interface Guidelines, the Paper Prototype helps to refine the flow, which reduces the changes required to the model and the view in later stages.

The Resources section at the end of this article contains links to all the tools and techniques described here.

Get accustomed to using a UI editor. NetBeans has a very good Swing editor. It's just a drag-and-drop affair. But there is one thing you must be wary of: generated code! After you create the UI, edit the code and remove all generated methods. Keep your UI free of any logic. Keep it stupid. Promote all your member fields in the classes to public. Now, I'm sure there are a lot of you who will raise an objection to this. Saying that this is anti-OOP. If you read a little further ahead, you will see my point.

Keep all your UI/view code in a separate package like blackmamba.ui. After the UI design, fire up your favorite Java editor (mine is Eclipse) and create a package for the control, such as blackmamba.ui.control. This is where you write all your UI logic. I like to name my control classes as verbs (Start, Login, Configure, etc.) and my view classes as nouns.

This kind of naming makes it easier to decide where to place your logic for each set of widgets participating in a UseCase. The view is completely free of code. Any changes to the layout or model can be done from the control. The UI contains no data that needs to be hidden. It is not something like a DepartmentBudget class whose budget field must be read-only to the CompanyBudgetReporter class, something accomplished by using a getter-only method and a private-access modifier for the budget field. For a widget, such security is not needed. In fact, there is not much security at all. The code below prints the class names of the entire UI tree. One can easily navigate to a JTextField and change its contents by casting the JComponent to JTextField and invoking the setText(...) method.

Container root = frame.getContentPane();
printClassNamesOfChildren(root);

void printClassNamesOfChildren(Container cont)
{
  for(int i=0; i < cont.getComponentCount(); i++) 
  {
    Component comp = cont.getComponent(i);
    if(comp instanceof Container) 
    {
      printClassNamesOfChildren((Container) comp);
    }
    else
    {
      System.out.println(
                       comp.getClass().getName());
    }
  }
}

The OO concept of data-hiding/encapsulation should be applied where it matters -- and it doesn't matter in the UI/view. Besides, look how ugly the code to access the widget's fields from the control would be: mailsPanel.getSpamMailsPanel().getList().add(...), as opposed to mailsPanel.spamMailsPanel.list.add(...). This technique is definitely not recommended for use anywhere outside the view. There is a Law of Demeter that says "Only talk to your immediate friends." This cannot be enforced completely in UI packages because the control classes have to know the internals of the view classes to be able to control them. A statement like mailsPanel.spamMailsPanel.list.add(...), where the mailsPanel is accessing the list's add(...) method (which is not directly available to the mailsPanel) would be a definite no-no in a non-UI context. Here, however, we are exempted.

The view is manipulated and managed entirely by the control. The view is not controlled from within but from the outside. To prevent any access to the UI from anywhere other than the control packages, a simple tool called Macker can be used to enforce such architectural rules. In the simple Macker configuration file shown below, access to view classes is denied to all classes except those in the control package and the view package itself.

<?xml version="1.0"?>
<macker>    
  <ruleset name="Layering rules">

    <pattern name="view"
     class="blackmamba.ui.view.**" />
    <pattern name="control" 
     class="blackmamba.ui.control.**" />
        
    <access-rule>
        <message>Only the Control can talk
                 to the View
        </message>

        <deny>
            <to pattern="view" />
            <allow> 
                <from pattern="view"/> 
            </allow>
            <allow>
                <from pattern="control"/>
            </allow>
        </deny>
    </access-rule>
       
  </ruleset>
</macker>

Pages: 1, 2, 3, 4, 5

Next Pagearrow