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

advertisement

AddThis Social Bookmark Button

A Generic MVC Model in Java
Pages: 1, 2

A Generic Model

As stated before, the TemperatureModel and HitModel classes are quite similar. Let us first try to factor out the code that handles the listeners of a model.



Both the TemperatureModelListener and HitModelListener interfaces each have one method, both of which do the same thing: notifying a listener about a change in a model. The names of these methods differ, but apart from that the only other difference is the type of their formal parameter: TemperatureModel and HitModel, respectively. That is exactly what generics are all about, so here is the code for a generic listener.

public interface ModelListener <M>
{
  void modelChanged (M model);
}

Although I do not like abbreviations, I use the generic formal type name M here. Using one-letter names for generic formal types is the convention, as explained in "Generics in the Java Programming Language" (PDF).

We then rewrite our listeners as shown below:

public interface TemperatureModelListener
extends ModelListener <TemperatureModel> { }

public interface HitModelListener
extends ModelListener <HitModel> { }

Both the TemperatureModel and HitModel classes manage listeners. Again, the only significant difference is the type of the listeners. So we might be tempted to write:

public class Model <L> ...

This, however, does not work, because the model makes a call to the listener's modelChanged () method. We have to make sure that a model is instantiated not with just any type, but with something that is a ModelListener. In generic speak, the formal type must be bound by ModelListener.

import java.util.*;

/**
* A generic MVC model.
*/
public class Model <L extends ModelListener>
{
  private final List <L> listeners;

  public Model ()
  {
    this.listeners = new ArrayList <L> ();
  }

  public void addModelListener (final L listener)
  {
    if (! this.listeners.contains (listener)) {
      this.listeners.add (listener);
      notifyModelListener (listener);
    }
  }

  public void removeModelListener (final L listener)
  {
    this.listeners.remove (listener);
  }

  protected void notifyModelListeners ()
  {
    for (final L listener : this.listeners) {
      notifyModelListener (listener);
    }
  }

  protected void notifyModelListener (final L listener)
  {
    listener.modelChanged (this);
  }
}

Being able to subclass this Model considerably reduces the size of our models. Shown below is the revised TemperatureModel.

public class TemperatureModel
extends Model <TemperatureModelListener>
{
  public enum Temperature {cold, hot};

  private Temperature temperature;

  public TemperatureModel ()
  {
    this.temperature = Temperature.cold;
  }

  public void setTemperature
     (final Temperature temperature)
  {
    this.temperature = temperature;
    notifyModelListeners ();
  }

  public Temperature getTemperature ()
  {
    return this.temperature;
  }
}

Some minor changes to the code of the client of our models are needed. For example, instead of calling hitModel.addHitModelListener(), we now call hitModel.addModelListener(). The complete mvc-generic-model.zip example is available in the Resources section below.

We managed to factor out the listener-handling code. This is an improvement, as it allows us to remove four methods from each model. But can we do better still? In fact, we can, because our two models still have quite a few things in common. Let's look at a generic property.

Generic Properties

The TemperatureModel has a temperature attribute, and the HitModel has a number attribute. Apart from their types, the attributes are quite similar. Again, this makes for a good candidate for a generic solution. Our first attempt might look something like:

public class Property <T> ...

where T stands for the type of the property. However, the setter methods make a call to the model's notifyModelListeners () method. A property needs a reference to the model of which it is a property. Thus, we might come up with something like the following:

public class Property <Model, T> ...

where Model stands for the model of which the property is a property. But there is a more elegant solution: make the Property class a non-static inner class of Model. Here is the code:

protected class Property <T>
{
  private T value;

  public Property (final T initialValue)
  {
    this.value = initialValue;
  }

  public void setValue (final T value)
  {
    this.value = value;
    notifyModelListeners ();
  }

  public T getValue ()
  {
    return this.value;
  }
}

This further reduces the size of our models. Below is the final version of our TemperatureModel:

public class TemperatureModel extends
        Model <TemperatureModelListener>
{
  public enum Temperature {cold, hot};

  public final Property <Temperature> temperature;

  public TemperatureModel ()
  {
    this.temperature
       = new Property <Temperature> (Temperature.cold);
  }
}

At first glance, the declaration of the temperature property seems to violate one of the most basic rules of class design: make instance variables private. In this case, however, this is not an issue, because the temperature variable is declared final, and the temperature variable itself does not have any public instance variables. Non-static inner classes model composition. They can be seen as dynamic extension of object state. If the object itself is public, then in general, there is no problem in its dynamic extensions being public, too. For more information on inner classes, see the excellent "Nested Classes, Part 1," by Robert Simmons, Jr.

Client code involving properties needs some minor modifications: e.g., instead of calling temperatureModel.getTemperature(), we now make a call to temperatureModel.temperature.getValue(). This example is available as mvc-general-property.zip in the Resources section below.

Constrained Properties

The Property class is very simple. It allows a client to get and set the value, nothing more. Sometimes, properties are a little more complicated. What if, for example, the model imposes a constraint on the value of a property?

Constraints on the value of a property can be easily implemented by adding a custom setter method to an anonymous subclass of the instantiation of the Property class. For example, the code below shows how to limit the hit count of our HitModel to a maximum of 10.

public class HitModel extends Model <HitModelListener>
{
  public final Property <Integer> number;

  public HitModel ()
  {
    this.number
       = new Property <Integer> (0)
         {
           public void setValue (final Integer value)
           {
             super.setValue (Math.min (10, value));
           }
         };
  }
}

Conclusion

At the start of this article, the TemperatureModel class had one constructor, four public methods, two private methods, and two private instance variables. By making the TemperatureModel class a subclass of the generic Model class, we managed to reduce that to one constructor and one public instance variable. That's progress!

Resources

Arjan Vermeij is a senior software engineer and Principal Scientific Assistant at the Nato Undersea Research Center


Return to ONJava.com.