HMVC Tutorial
From thecentric
Contents |
Download
java-gnome-hmvc.tar.gz - This includes as seperate source trees the HMVC library, a Gtk-HMVC example implementation and the example Hello World application shown below.
HMVC - Hierarchical Model-View-Controller
This is a quick run through and HOWTO for using my pet Java HMVC framework. Let me firstly say that it was originally created by a team of people, including myself, while working at 3Q Solutions. It is a view / implementation (SWT, Swing, AWT, Java-Gnome) independent framework - that primarily helps with the structured managing and creating of Client Desktop applications.
It is basically an event listening and event firing framework framework that lets you have a clean separation between your model/domain and your view. There is the potential for your views and models to be reusable in your application and further it allows your view/model to ‘communicate’ with any other other part of you application by just firing an event. It tries to answer the following questions:
- What’s a good consistent way of structuring my GUI?
- What’s a good way to get a clean separation between my GUI and my Domain?
- What’s a good mechanism to provide widget control and application flow?
The good news is, that this framework has no external dependencies except to JUnit for some tests.
Lets get you introduced quickly to the different Java HMVC classes that exist in this framework and what they do. This will be helpful for a broad understanding; before diving into how they are all used together. Firstly here are the event and listener classes:
HmvcEvent
HmvcEvent is the event that gets fired, from either a view (on for example the click of a button) or from a model (on for example the addition of an object [e.g. Customer] into your domain/db). Your instance of the HmvcEvent object gets fired around the application. In HmvcEvent is a base class, and you create your own event by extending it - so you might create a CustomerAddedToDatabaseEvent or else maybe a NewCustomerButtonClickedEvent. In most cases, you just need to let another part of the system know that the NewCustomerButtonClickedEvent occured, but in some you also may wish to embed some data inside the event. So HmvcEvent optionally takes an Object payload.
Customer customer = new Customer("Sean");
HmvcEvent event = new CustomerAddedToDatabaseEvent();
event.setPayload(customer);
//and then later we fire the event!
HmvcEventListener
HmvcEventListener is an interface that you will implement to listen for a particular type of event. It only contains one method: public void handleEvent(HmvcEvent event);
An implementation of this listener might be called NewCustomerButtonClickedEventListener and look like this:
public void handleEvent(HmvcEvent event) {
// do some jazz like popping up a New Customer dialog
// or else add a new customer to the domain
}
Very simple so far right? Next we’re going to deal with the Controller, Model and View.
Controller
The Controller class has two methods that you will be interested, fireEvent and register listener. A Controller ultimately handles the event firing and the event registering.
fireEvent(HmvcEvent event) register(Class hmvcEventType, HmvcEventListener listener)
Hopefully, this is pretty self explanatory - but in order to fire an event throughout the system, you call fireEvent passing in the event you want to propagate around the application. An example would be our NewCustomerButtonClickedEvent above. In order to register for the firing of an event you call through to register; passing in the event class e.g. NewCustomerButtonClickedEvent.class and also the listener you have created to handle the particular event e.g. the NewCustomerButtonClickedEventListener above.
The methods above are the two methods you will use the most off of Controller, some of the other interesting ones are: deregister() for deregistering listeners. fireDown() for firing to the controllers children only, and not to its parents (more on this later) dispose() for clearing the controllers listeners and unhooking the controller it’s parent.
Model
Model is a base class that you will more than likely be extending to handle the receipt of events and do various model / domain code based on the event type and the payload in the event. It is automatically configured to have a controller (more on how this occurs later) and has two delegates to the methods in it’s Controller. These methods are the ones I mentioned above, namely fireEvent and register listener. To register listeners inside of a Model it is best done by overring a method called post() (this will get called after the initialisation of the Model) and register for the event type inside of there. So extending our customer metaphor we might have the simple Model below:
public class CustomerModel extends Model
{
List _customers = new ArrayList();
public void post() {
register(EditItemEvent.class, new NewCustomerButtonClickedEventListener(this));
};
public void addCustomer(Customer customer) {
_customers.add(customer);
fireEvent(new CustomerAddedToDatabaseEvent());
}
}
public class NewCustomerButtonClickedEventListener implements HmvcEventListener
{
CustomerModel _backPointer;
public NewCustomerButtonClickedEventListener(CustomerModel backPointer) {
_backPointer = backPointer;
}
public void handleEvent(HmvcEvent event){
_backPointer.getCustomers().add(new Customer());
}
}
View
View is an interface because it should be implemented by the type of widget architecture you are using, for example SWT, Swing, Java-Gnome etc… Because, at time of writing, alot of my hobby work has been dedicated to Java-Gnome, below is an example of a VBox in Java-Gnome implementing a View: Any methods that are missing from the following example, can be left with a blank implementation for the purposes of getting an example up and running. doInit() would be the method I’d recommend to override for adding children widgets, and I would recommend overriding post() to register event listeners.
public class GtkVBox extends VBox implements View
{
private Controller _controller = new NullController();
protected Container _parent;
private ControlName _controlName = new NullControlName();
public GtkVBox(Container container, boolean homogenous, int spacing) {
super(homogenous, spacing);
_parent = container;
}
final public void init() {
pre();
doInit();
post();
}
public void pre() { }
public void post() { }
public void doInit() {}
public void doShow() {
_parent.add(this);
_parent.showAll();
}
public void setController(Controller controller) {
_controller = controller;
}
public Controller getController() {
return _controller;
}
public void dispose() {
destroy();
_parent.remove(this);
}
protected void closeView() {
getController().dispose();
if (_parent instanceof Window)
_parent.destroy();
else
getParentWindow().destroy();
}
}
Finally lets see how the Model, View and Controller are hooked up - and let’s also find out where this Hierarchical word fits in. Out last class to cover is the Triad object which is responsible for hooking up and configuring the M, V and C! But don’t worry - it’s very easy to use!
Triad
Let’s look at two different ways of constructing a new Triad()
public Triad(Model model, View view, Controller controller) public Triad(Controller parent, Model model, View view, Controller controller)
The first constructor above takes in a single Model, View and Controller. The second constructor also takes a parent controller - which is where the Hierarchical finally comes in! As soon as a Triad() is constructed - the following build process takes place:
- If constructed with a parent controller, the Triad adds the Controller as a child to the parent.
- The Controller is set on the Model and View (i.e. _model / _view.setController(_controller))
- The Model and View are set on the Controller (_controller.setModel(_model) / .setView(_view))
- Finally init() is called on each Model, Controller and finally View.
- init() inside Model, Controller and View in turns calls through to pre(), doInit() and post()
A Triad may be considered transient; just used to hook up and initialise it’s components. But if you do keep it around you can call destroy() on your parent triad, and this will propagate down a destroy() to all it’s children.
HMVC Visualised
The above diagram I borrowed from a JavaWorld HMVC article. It does a good job of explaing how the triads are connected through their controllers. Even though the diagram and the JavaWorld article imply a very layered approach I prefer to think of my HMVC framework as an embedded approach where a root triad contains triad children.
HelloWorld
java-gnome-hmvc.tar.gz - Again; this contains the HMVC source, a simple gtk-hmvc implementation and a helloworld HMVC example. Above is a screenshot of the helloworld application - which contains a root and an embedded triad. A fuller example is one of my older projects which ties in Glade to the HMVC framework. This application is called JBalorsEye.
Contact
excentric@gmail.com - Sean Coughlan