MVC in GUIDE
The Model-View-Controller pattern is primarily defined by it's aim of separating concerns:
-
Model
Encapsulates some domain-specific data (usually as a POJO or JavaBean in Java) -
Controller
Responds to user-events, possibly updating the view or the model as a result. -
View
A form comprising several controls with which to view or edit the model. In Swing, this is a JDialog or JFrame containing Components.
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:
- GUI Architectures - Fowler - A broad overview of GUI patterns
- Passive View - Fowler - An MVC pattern where all logic is in the controller. The view and model are completely separated with the controller as a mediator.
- Supervising Controller - Fowler - An MVC pattern in which the controller contains complex logic. Simple mappings between view and model are handled directly.
- Interactive Application Architecture Patterns - Greer - Another broad look at MVC-type patterns, their history and different implementations.
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:
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:
- Display a read-only
target value. - Allow the user to edit the
actual value. - When the user presses enter, calculate and show the updated variance. variance = target - actual
- If the variance is less than -5, display it in red.
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:
There are two advantages to this approach:
- It's very simple to understand.
- It's easy to test.
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:
- The Model - the problem is well described so we can start here
- The Controller - we already know what the view should look like, so we can build the controller first.
- The View - the final part in our MVC triumvirate. We build the view in GUIDE bind it to the controller.
- 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:- Display a read-only
target value. - Allow the user to edit the
actual value. - When the user presses enter, calculate and show the updated variance. variance = target - actual
- 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.
Given the view above, we can describe our controller rules in terms of the view:
- Set
targetTextField's value from the model'stargetproperty. - Respond to an
actionPerformedevent on theactualTextField - Update the model's
targetproperty from thetargetTextField - Update the
varianceTextFieldfrom the model'svarianceproperty - Update the
varianceTextField'scolour from the model'svarianceproperty
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
- References to the view's text fields will null at the time of construction
- GUIDE needs a no-arg constructor
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).

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.

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.
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 thepassiveView
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:
The advantages and disadvantages of this over Passive View are:
- It's quicker to program.
- GUIDE does more for you, reducing human error.
- 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:
- Add
PropertyChangeSupportto our model (this is how our view receives updates) - Simplify our controller (the controller only needs to worry about colour now)
- 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 thecontroller 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!
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.
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.