Newsletter sign-up
View all newsletters

Enterprise Java Newsletter
Stay up to date on the latest tutorials and Java community news posted on JavaWorld

Sponsored Links

Optimize with a SATA RAID Storage Solution
Range of capacities as low as $1250 per TB. Ideal if you currently rely on servers/disks/JBODs

HMVC: The layered pattern for developing strong client tiers

This hierarchical model eases the development of a Java-based client tier

  • Print
  • Feedback

Page 4 of 5

The model encapsulates key server-interaction functionality to retrieve data. To accomplish this task, key issues -- SQL requests, data element-mapping between GUI and databases, transport data formats, etc. -- need to be redressed. Design issues, like data-persistence mechanisms in the model need to be ironed out. However, in most thin-client architectures, the role of the model tends to be that of a data conduit that hides the complexity of data fetching and decouples the rest of the client tier from server-side data formats.

Let's go for a spin

A simple example will help you grasp key concepts.

Figure 4. HMVC client tier instance diagram



Figure 4 illustrates an instance diagram of a client tier, with some of its key components and layers. As explained earlier, the hierarchy of MVC triads for GUIContainers, GUIFrames, and GUIPanes are apparent in this diagram. The model classes in each MVC layer encapsulate the details of the data formats and are responsible for interacting with the ServerFacade to satisfy data requests. The server-side and transport-specific format is mapped to this data format, thereby decoupling the application data formats from its GUI representation.

The Hello World application

In our sample application, we will try to see how the HMVC makes sense in crafting a robust client tier. Our application consists of a GUIContainer, the highest MVC triad, consisting of GUIContainerController, GUIContainerModel, and GUIContainer. The GUIContainer is a nonvisual view; it simply provides services to the contained GUIPanes. In terms of importance within the triad, the controller is assumed to be at a higher level of responsibility than the model and view. It is their gatekeeper; the controller has references to the model and view. GUIFrameController, GUIFrame, and GUIFrameModel compose the second rung. A collection of GUIPanes (see Figure 2), namely MenuGUIPane, StatusGUIPane, NavGUIPane, and ContentGUIPane, make up the GUIFrame. Since the GUIPanes are fairly trivial (ContentGUIPane excepted), they don't have their own controller. Instead, the GUIFrameController controls them. Your needs may be different, however, and each GUIPane could have its own triad if necessary.

We must distinguish between an HMVC controller and a Swing listener. In order to isolate Swing code, each GUIPane by itself deals with Swing events. For example, look at the class definition of the MenuGUIPane:

public class MenuGUIPane extends javax.swing.JMenuBar implements ActionListener


Figure 5. Sample application screenshot



Thus, the MenuGUIPane listens for the ActionEvent generated when a user clicks the File, Hello World menu item (Figure 5). The MenuGUIPane then determines what service it wants from its controller, the GUIFrameController. In our particular example, the ActionEvent replaces the ContentGUIPane, so a navigation service is expected. The code snippet below illustrates the creation of the navigation AppEvent object and its transmission to the GUIFrameController (ctlr_).

public class MenuGUIPane ...
{
   ...
   public void actionPerformed(ActionEvent actionEvent)
   {
      if(actionEvent.getActionCommand().equals("HELLO"))
      {
         ...
         AppEvent ae = new AppEvent(AppEvent.NAV_EVENT);
         ae.setMessage("SHOW_HELLO_WORLD");
         ctlr_.handleAppEvent(ae);
      }
      else
      if(actionEvent.getActionCommand().equals("CLOSE"))
         System.exit(0);    
  }
  ...
}


Upon receiving the AppEvent, GUIFrameController knows what to do in the case of a navigation event. (Our example is fairly trivial, meant to show key behavior and components. In an actual application, more would need to be done!) It creates a new child MVC triad composed of the ContentGUIController, ContentGUIPane, and the ContentGUIPaneModel, and establishes the parent-child hierarchy structure.

public class GUIFrameController ...
{
   ...
   public void handleAppEvent(AppEvent ae)
   {
      if(ae.getMessage().equals("SHOW_HELLO_WORLD"))
      {
         createChildTriad();
         childCtlr_.init();
      }
   }
 
   ...
   public void createChildTriad()
   {
      ContentGUIPane contentPane = new ContentGUIPane(new Dimension(20,20));  
      ContentGUIPaneModel contentModel = new ContentGUIPaneModel();
      ContentGUIPaneController contentController = new ContentGUIPaneController();
        
      contentController.setView(contentPane);
      contentController.setModel(contentModel);
        
      contentController.setParentController(this);
      setChildController(contentController);
      view_.setPane(contentPane);
   }
}


Figure 6. Sample application screenshot



Figure 6 shows the resulting GUIFrame. In order to further illustrate some key concepts behind the responsibility-based controller hierarchy and MVC interaction, let us take a look at intralayer communication like retrieving data -- data-event handling, in other words.

Figure 7. Sample application screenshot



Upon clicking the Click button, the text "Hello World!" appears in the TextField, as in Figure 7. The mechanism for achieving this is similar to what transpired in the MenuGUIPane actionPerformed() method. However, this time, instead of a navigation service, the ContentGUIPane controller requests a data service.

public class ContentGUIPane ...
{
   ...
   public void actionPerformed(ActionEvent ae)
   {
      if(ae.getActionCommand().equals("CLICK"))
      {
         ...
         AppEvent ape = new AppEvent(AppEvent.DATA_EVENT);
         ape.setMessage("HELLO_WORLD");
         ctlr_.handleAppEvent(ape);
      }
      ...
   }
   ...
}


The ContentGUIPaneController simply delegates a data-service event to its associated model object; status-event or navigation-event service would be passed on to its parent controller, the GUIFrameController. This establishes a clear chain of responsibility for the controllers. They deal only with events that are within their scope and defer other events to their parents.

public class ContentGUIPaneController...
{
   ...
   public void handleAppEvent(AppEvent evt)
   {
      if(evt.isDataEvent())
         contentModel_.handleAppEvent(evt);
      else
      if(evt.isStatusEvent() || evt.isNavEvent())
         parentCtlr_.handleAppEvent(evt);
   }
   ...
}


Upon receiving the data-service event, the ContentGUIPaneModel would engage the ServerFacade to retrieve data and subsequently request the associated view to refresh itself. (The ServerFacade is not implemented in the example as it is out of this article's scope.)

  • Print
  • Feedback

Resources