MVC in GUIDE

The Model-View-Controller pattern is primarily defined by it's aim of separating concerns:

For simplicity's sake, we'll be referring to the above concept as a pattern, but MVC is really collection of related patterns, each adhering to the aim of separating concerns, but differing in their approach. Martin Fowler and Derek Greer have approached this topic with rigour, and it's worth reading their essays if you have time:

MVC in Swing

MVC in Swing is a strange beast. Most GUI toolkits have provided clear MVC-type guidance on how to present and edit domain data with their widget toolkits. Widgets (or Components in Swing) are merely tools for interacting with the user (buttons, fields, etc). Their internals are not typically of any concern.

Swing however, took the rather odd step of introducing an MVC separation into their widgets. It makes some degree of sense to extract a model for JTables, JTrees and JLists, but the approach is stamped into almost every component. For example, a JButton consists of a ButtonModel class (the model), a JButton class (the controller, cross-platform view stuff) and a ButtonUI class (the view, with LAF-specific controller stuff). Swing does a fair job of separating out a model, but controller and view responsibilities are usually shared between the JComponent and the UI delegate. Most of the time, this aspect of Swing is of little concern to users of the toolkit, and the use of MVC parlance just confuses things. For the rest of this article, any mention of MVC will refer only to the problem how to model forms to present and manipulate user data; an area in which Swing provides no guidance.

MVC in GUIDE - an example

Note: The following two examples are included in the Sample Project availble from the Welcome Screen.

As mentioned earlier, there is no single MVC pattern, but rather a multitude which share a common objective. All MVC patterns aim to separate domain data from presentation logic and visualisation, but they differ in their approaches. In this article, we seek to implement Martin Fowler's example application of the ice-cream particulate database. The view we'll be building looks like this:

Icecream Particulate Database Note: This is very contrived example taken from Martin Fowler's MVC articles. Don't worry too much about why anyone would want to build such a thing, but rather focus on the simple set of rules listed below. According to Fowler's articles, the example application has to do the following:
  1. Display a read-only target value.
  2. Allow the user to edit the actual value.
  3. When the user presses enter, calculate and show the updated variance. variance = target - actual
  4. If the variance is less than -5, display it in red.
We'll be implementing this using two patterns described by Fowler:

Passive View in GUIDE

The Passive View pattern, as described by Fowler, aims to keep the view as simple as possible. The view is completely isolated from the model. The controller is responsible for populating the view, responding to events and mediating between the view and the model. Our class relationships look something like this:

Passive View Class Diagram

There are two advantages to this approach:

  1. It's very simple to understand.
  2. It's easy to test.
Point 2 is important. Many people have poured countless hours into trying to test their forms using the view, whether by using golden images or robot scripts. By putting all your logic into the controller, you can easily write headless unit tests without resorting to any magic.

Where to start

It is always difficult to know where to start with these examples. Which came first: the model or the view? For this well described problem, we going to implement the Passive View in the following order:

  1. The Model - the problem is well described so we can start here
  2. The Controller - we already know what the view should look like, so we can build the controller first.
  3. The View - the final part in our MVC triumvirate. We build the view in GUIDE bind it to the controller.
  4. JUnit test - one of the main reasons for going with the Passive View approach is to write comprehensive tests, so we do that too.

The Model

Our model class is really is simple Java object, with a hint of domain logic. There's really not much to say here. The domain object should be simple.

public class Model {
    private int target;
    private int actual;

    public Model(int target, int actual) {
        this.target = target;
        this.actual = actual;
    }

    public int getTarget() {
        return target;
    }

    public int getActual() {
        return actual;
    }

    public void setActual(int actual) {
        this.actual = actual;
    }

    public int getVariance() {
        return actual - target;
    }
}

The Controller

The controller is where all the action happens in a Passive View design. Have a look at what the controller has to do again:
  1. Display a read-only target value.
  2. Allow the user to edit the actual value.
  3. When the user presses enter, calculate and show the updated variance. variance = target - actual
  4. If the variance is less than -5, display it in red.

We need to apply these rules to our view. Have a look at the view again.

View with field names

Given the view above, we can describe our controller rules in terms of the view:

  1. Set targetTextField's value from the model's target property.
  2. Respond to an actionPerformed event on the actualTextField
    1. Update the model's target property from the targetTextField
    2. Update the varianceTextField from the model's variance property
    3. Update the varianceTextField's colour from the model's variance property
The skeleton of our controller would look something like this:
public class Controller {
    private Model model;

    public Controller(Model model) {
        this.model = model;
        targetTextField.setValue(model.getTarget());
        actualTextField.setValue(model.getActual());
        updateVariance();
    }

    public void actualUpdate() {
        model.setActual((Integer) actualTextField.getValue());
        updateVariance();
    }

    private void updateVariance() {
        varianceTextField.setValue(model.getVariance());
        varianceTextField.setForeground(model.getVariance() < -5 ? RED : BLACK);
    }
}

This is fine for a start, but missing from the above code is how we get references to view's three JTextFields. We can accomplish this by providing setters on controller and letting the view inject the values into the controller.

We can add the following into GUIDE:

public class Controller {
    private JFormattedTextField targetTextField;
    private JFormattedTextField actualTextField;
    private JFormattedTextField varianceTextField;

    // Code from above ommitted for brevity

    public void setTargetTextField(JFormattedTextField targetTextField) {
        this.targetTextField = targetTextField;
    }

    public void setActualTextField(JFormattedTextField actualTextField) {
        this.actualTextField = actualTextField;
    }

    public void setVarianceTextField(JFormattedTextField varianceTextField) {
        this.varianceTextField = varianceTextField;
    }
}

There are still two problems remaining with our controller

  1. References to the view's text fields will null at the time of construction
  2. GUIDE needs a no-arg constructor
We can fix this by moving our initialisation of the view to an init method and creating a no-arg constructor. Our init method will need to called after the view has injected it's JTextFields into the controller. Our final controller looks like this:
    public class Controller {
        private Model model;

        private JFormattedTextField targetTextField;
        private JFormattedTextField actualTextField;
        private JFormattedTextField varianceTextField;

        public Controller() {
            this(new Model(42, 39));
        }

        public Controller(Model model) {
            this.model = model;
        }

        public void init() {
            targetTextField.setValue(model.getTarget());
            actualTextField.setValue(model.getActual());
            updateVariance();
        }

        private void updateVariance() {
            varianceTextField.setValue(model.getVariance());
            varianceTextField.setForeground(model.getVariance() < -5 ? RED : BLACK);
        }

        public void actualUpdate() {
            model.setActual((Integer) actualTextField.getValue());
            updateVariance();
        }

        public void setTargetTextField(JFormattedTextField targetTextField) {
            this.targetTextField = targetTextField;
        }

        public void setActualTextField(JFormattedTextField actualTextField) {
            this.actualTextField = actualTextField;
        }

        public void setVarianceTextField(JFormattedTextField varianceTextField) {
            this.varianceTextField = varianceTextField;
        }
    }

The View

In GUIDE, the view is the easy part. If you look back to the class diagram for Passive View, you'll see that View references the controller. At minimum, it needs this reference for the actionPerformed event which we will handle in the controller. In GUIDE, we do this by passing in the controller as a parameter to the view's factory method (or constructor, depending). You can specify this when you create the form (or simply edit the method directly in your IDE).

Passive View - injecting the controller

Next, we need to tell GUIDE which JTextFields should be injected into which of the controller's setters. GUIDE displays the controllers properties in the top right hand corner. At the moment these are listed as null. We can set these fields by simply Alt-dragging (or Command-dragging on the Mac) each JTextField on the relevant setter.

Passive View - injecting the controller

If you take a look at the view that GUIDE saves, you will see references such as:

    JFormattedTextField formattedTextField0=new JFormattedTextField();
    formattedTextField0.setBounds(new Rectangle(77,12,110,28));
    formattedTextField0.setColumns(8);
    formattedTextField0.setEditable(false);
    controller.setTargetTextField(formattedTextField0);

Lastly, we need to hook up actualUpdated to the actionPerformed event fired from the actualTextField. It is simply a matter of dragging the method from "Actions" tab onto the textfield to create the appropriate event handler. As an extra, we can also get GUIDE to call the init method for us, after it has called the setters.

Passive View - injecting the controller

At this stage, we've completed all three classes of our MVC pattern. A sample of how this would called

public class Main {
    public static void main(String[] args) {
        Controller c = new Controller(new Model(45, 40));
        JFrame frame = View.create(c);
        frame.setVisible(true);
    }
}    

Testing a Passive View Controller

One of the main reasons for following a Passive View pattern, is to allow for easy testing of the controller. Here is a complete example JUnit 4 test:
public class ControllerTest {
    private JFormattedTextField targetTextField;
    private JFormattedTextField actualTextField;
    private JFormattedTextField varianceTextField;

    @Before
    public void setUp() {
        targetTextField = new JFormattedTextField();
        actualTextField = new JFormattedTextField();
        varianceTextField = new JFormattedTextField();
    }

    @Test
    public void testInit() {
        Model model = new Model(40, 30);
        Controller controller = new Controller(model);

        controller.setTargetTextField(targetTextField);
        controller.setActualTextField(actualTextField);
        controller.setVarianceTextField(varianceTextField);

        controller.init();

        assertEquals("Target textfield not set up correctly", targetTextField.getValue(), model.getTarget());
        assertEquals("Actual textfield not set up correctly", actualTextField.getValue(), model.getActual());
        assertEquals("Variance textfield not set up correctly", varianceTextField.getValue(), model.getVariance());
        assertEquals("Variance color should be red", varianceTextField.getForeground(), Color.RED);

        actualTextField.setValue(37);
        controller.actualUpdate();

        assertEquals(37, (Object) model.getActual());
        assertEquals(Color.BLACK, varianceTextField.getForeground());
    }
}

This test covers all of the applications requirements without needing to mess around with the view. With the Passive View pattern, there really is very little need to test the view. If it works in GUIDE, then it should just work. As long as your controller (and potentially your model) are tested, you should have more than adequate coverage.

Note: The Sample Project contains both the code used above (in the passiveView package, and another approach in the passiveViewMock package. The latter takes the Passive View pattern to the next level, by extracting an implementation-independent View interface. Aside from isolating the controller from the view's internals, it also allows one to mock the view when writing unit tests for your controller.

Supervising Controller in GUIDE

The Supervising Controller pattern, as desribed by Fowler, is an easing of restrictions of the Passive View pattern. We allow the view to reference the model through simple bindings. User actions (other than those handled by bindings), are still handled in the controller. As long as your data bindings are working, you are still able to test the important functionality by writing unit-tests for your controller. Our class relationships now look like this:

Supervising Controller Class Diagram

The advantages and disadvantages of this over Passive View are:

  1. It's quicker to program.
  2. GUIDE does more for you, reducing human error.
  3. You're leaving responsibility for view-updates to GUIDE making these more difficult to test

Continuing the example

For this part of the article, we'll take the same example from before, implement it using the Supervising Controller pattern. The primary difference between the two patterns, is that we're now allowed to update our view directly from the model. GUIDE can generate one-way or two-way bindings depending on whether the model is bound or not. The outline of our next steps are:

  1. Add PropertyChangeSupport to our model (this is how our view receives updates)
  2. Simplify our controller (the controller only needs to worry about colour now)
  3. Use GUIDE to create new view and link up model bindings

The Model

The model is very similar to the passive view model, except that we need to keep the view synchronized with the model this time. We add PropertyChangeSupport to the model and fire propertyChangeEvents when any of our properties change.

public class Model {
    private int target;
    private int actual;
    private int variance;

    private PropertyChangeSupport propertyChangeSupport = new PropertyChangeSupport(this);

    public Model() {
        this(43, 23);
    }

    public Model(int target, int actual) {
        this.target = target;
        this.actual = actual;
        updateVariance();
    }

    public int getTarget() {
        return target;
    }

    public int getActual() {
        return actual;
    }

    public void setActual(int actual) {
        int oldValue = this.actual;
        this.actual = actual;
        propertyChangeSupport.firePropertyChange("actual", oldValue, actual);
        updateVariance();
    }

    public int getVariance() {
        return actual - target;
    }

    private void setVariance(int variance) {
        int oldValue = this.variance;
        this.variance = variance;
        propertyChangeSupport.firePropertyChange("variance", oldValue, variance);
    }

    private void updateVariance() {
        setVariance(actual - target);
    }

    public void addPropertyChangeListener(PropertyChangeListener listener) {
        propertyChangeSupport.addPropertyChangeListener(listener);
    }

    public void removePropertyChangeListener(PropertyChangeListener listener) {
        propertyChangeSupport.removePropertyChangeListener(listener);
    }

    public PropertyChangeListener[] getPropertyChangeListeners() {
        return propertyChangeSupport.getPropertyChangeListeners();
    }
}    
Note: Highlighted code can easily be extract to in an AbstractBean super-class to increase readability. You may even have one already in your classpath considering how boiler plate it is.

The Controller

In the Supervising Controller pattern, the controller is relieved of maintaining simple view state; we can let GUIDE do that next. But the controller still needs to respond to the actionPerformed event and update the varianceTextField's colour. We can take the controller from the passive view example and make merry with the delete key:
public class Controller {
    private Model model;
    private JFormattedTextField varianceTextField;

    public void setModel(Model model) {
        this.model = model;
    }

    public void setVarianceTextField(JFormattedTextField varianceTextField) {
        this.varianceTextField = varianceTextField;
    }

    public void init() {
        varianceUpdate();
    }

    public void varianceUpdate() {
        varianceTextField.setForeground(model.getVariance() < -5 ? RED : BLACK);
    }
}    

From the above example, you can see the allure of this pattern. We could even remove the init method and tell GUIDE to use the varianceUpdate method instead, bringing the controller down to 16 lines!

Note: We could even have made the colour a bound property of the controller and simply bound the foreground property of the varianceTextField to a colour property of the controller. It can be done in GUIDE, an is a simple concept to understand, but would have bloated this example with property change boiler-plate code.

The View

For this example, we'll create a new view. This time we'll need to pass in a reference to both the model and the controller. We can go as before, creating the form from our component palette and dragging properties on them. But GUIDE can automatically generate labelled and fully bound properties simply by dragging properties directly onto your layout. You can even drag the whole model bean to autogenerate controls from all of its properties.

Supervising Controller - creating the view

And we're done. Our main method looks like this now:

public class Main {
    public static void main(String[] args) {
        Model model = new Model(40, 20);
        JFrame view = View.create(new Controller(), model);
        view.setVisible(true);
    }
}

Testing a Supervising Controller

Because we've handed off a lot of boiler-plate code to GUIDE now, our unit test is correspondingly smaller. View state is now handled by GUIDE, so we just need to make sure the varianceTextField gets updated with the correct colour. Our new unit test now look like:

public class ControllerTest {
    private JFormattedTextField varianceTextField;

    @Before
    public void setUp() {
        varianceTextField = new JFormattedTextField();
    }

    @Test
    public void testVarianceColor() {
        Model model = new Model(40, 30);
        Controller controller = new Controller();

        controller.setModel(model);
        controller.setVarianceTextField(varianceTextField);
        controller.init();

        Assert.assertEquals(Color.RED, varianceTextField.getForeground());
        model.setActual(37);
        controller.varianceUpdate();
        Assert.assertEquals(Color.BLACK, varianceTextField.getForeground());
    }
}

There's a bit less testing going on this time, but it is more focused on the problem domain. Which pattern suits you is more a matter of personal taste than anything else.

Conclusion

MVC patterns are in important tool in separating concerns. There are different flavours of MVC patterns around, and in this article, we have only looked at two. If you're interested in learning more, it's worth looking at Martin Fowler and Derek Greer's essays on the topic mentioned at the beginning of this article.

GUIDE doesn't force any particular pattern on the user, but it does try to make it easy to use them. For those who prefer the Supervising Controller pattern, GUIDE makes this especially easy with it's automatic component generation and binding tools.


Brendon McLean - 31 March 2009