Using Presentation-abstraction-control (PAC) and Hierarchical-Model-View-Controller (HMVC) with Java and Swing

There is a useful introduction to these two patterns at http://en.wikipedia.org/wiki/Presentation-abstraction-control, so I won't describe them again here. Instead, I want to discuss my experience of using these patterns in production, and the framework I have been experimenting with.

In my last development job, we used H-MVC on a large thick client Swing application and found it made it a lot easier to implement an application with very loose coupling between components. The application had about 60 controllers and 75 views. When we implemented H-MVC, we had already written much of this code using Presentation Model which we found didn't scale well. We stripped out jGoodies Binding and inserted H-MVC.

I was unhappy with the design and implementation of the framework (done by a colleague) we used and decided implement my own for my personal use. I have been using my experimental framework to implement a new version of Silk, my interactive JPA/QL query tool.

I have found using this framework makes it a lot easier to implement a moderately complex application with very little coupling between components. I think it is very likely that this approach would also significantly reduce coupling and ease implementation of a much larger application.

The problem with standard MVC

When, for example, ControllerF needs a service provided by ControllerA, simpler patterns generally assume ControllerF will make a method call on ControllerA. This means a reference to an instance of ControllerA will need to be passed to ControllerF. If ControllerF is created by ControllerE which is, in turn, created by ControllerD and so on, the reference to ControllerF will need to be passed down the hierarchy to every controller in the sequence. This leads to tight coupling between controllers which may otherwise need no knowledge of each other whatsoever. If Dependency Injection (generally a Good Thing) is used, you can end up with huge constructors.

The PAC/H-MVC solution

(I still haven't decided whether to call this PAC or H-MVC -- I generally use the two names interchangeably).

With PAC, a controller (and also a view) can make a request for a service without needing to know who will provide that service. Rather than making a method call to the provider, the requester sends the request to the PAC framework. The provider of the service will register a handler for each service it provides which will be called to service the request. The result of the request is then returned to the caller.

My PAC framework implements the code to handle requesting and providing services in the Controller super-class. Here is a snippet showing a Controller which registers a provider of a service:

  1. public class EditorController extends Controller {
  2. public EditorController() {
  3. register(new OpenFileProvider());
  4. }
  5.  
  6. private class OpenFileProvider extends
  7. Provider<EditorRequests.OpenFile> {
  8.  
  9. public String provide(EditorRequests.OpenFile request) {
  10. }
  11. }
  12. }

In its constructor, the EditorController registers a Provider of the EditorController.OpenFile request. The inner class OpenFileProvider will be called each time another controller or view requests the service. Here, a view requests the OpenFile service:

  1. String text = invoke(new EditorRequests.OpenFile());

Note that the request works a lot like a method call. The request knows what its return type will be and an instance of that type is passed back by invoke(). By making use of Java 5 parameterised types, the interface to the provider is very clean, involving no ugly casts.

The Provider is located by following the hierarchy of controllers -- each controller has a "parent" controller to which the request is passed if that controller doesn't have a handler. This way, Requests work their way up the hierarchy until a Provider is located. This happens quietly in the background -- the Controller super-class takes care of it.

The service request itself is a class which implements Request. I have grouped mine together as static inner classes to avoid having a huge number of source files:

  1. public class EditorRequests {
  2. public static class OpenFile extends Request<String> {
  3. }
  4. }

This is all the Request needs if it has no arguments -- the class is used by the PAC framework to uniquely identify the request type. Requests with input arguments can be easily implemented using constructor injection:

  1. public static class SetQuery extends Request<String> {
  2. private String text;
  3.  
  4. public SetQuery(String text) {
  5. this.text = text;
  6. }
  7.  
  8. public String getText() {
  9. return text;
  10. }
  11. }

Note that there isn't much of a model here -- the String returned by the Request is as close as we get in this example.

The request behaves much like an immutable bean (immutability is another Good Thing). Note that the Requests don't have to be immutable, I just choose to code that way.

This request is invoked like this:

  1. invoke(new EditorRequests.SetQuery(query));

The Provider of this service will receive the SetQuery instance and can simply call getText() to retrieve the argument.

My PAC/H-MVC framework is experimental and under development. Anyone who wants to take a look is welcome to download it. But please don't use it for production unless you are sure you know what you are getting. Comments and suggestions are welcome.

The download includes a (too) simple example. Documentation is very thin at the moment and there is no tutorial. If anyone actually wants to use this, I will continue to refine it and write some documentation.

The framework is published under the GNU General Public License.

Future directions

I think some kind of broadcast Request type would be useful. Rather than this Request being serviced by the first registered Provider, it would be passed to all providers of the Request type. This could be used, for example, to ask every registered Provider if the application could quit. This allows each interested Controller and View to pop up a Yes/No/Cancel dialog for each unsaved transaction. A Broadcast would be vetoable, so it would not be passed on to other Providers once it had been vetoed.

If there is sufficient interest in using this framework, I will document it better and write a tutorial.

Update Wed 22nd Oct: I have added a more complete (but still incomplete) example. The new example is the Silk query editor which may one day grow into a new version of my Silk/QL tool. It lacks useful comments as I'm learning about H-MVC and my framework as I go. If anyone wants to make use of the framework, let me know and I will provide better documentation. This example requires JDK 1.6 as it uses FileNameExtensionFilter.

I think the Silk example is just about large enough to show the strengths and weaknesses of H-MVC.

Jar file containing all the H-MVC source files babblemind-hmvc-src-0.1.jar.

To compile and run this you need JDK 1.6 or better. ant, junit and log4j. I am using Ubuntu Linux which installs these from some standard packages:

ant
sun-java6-jdk
liblog4j1.2-java
junit4

Other Linux distros will have similar packages available. I have no idea how to make it work on Windows, I'm afraid.