Back to Thoughts On Technology Page | Back to Peter's Home Page | Back to Family Home Page



Simple MVC Swing Communication Example
Peter Rose - 10/2009


Abstract

Summary

Background

The Controller As A Mediator

The Model Class

The View Class

The Glue That Holds It All Together

Completing The Controller

Simplicity Hides Potential Complexity

Conclusion





Abstract
Create a very simple example of how to separate a Swing application into MVC pattern components and show how these components communicate with each other.


Summary
If you've ever tried to figure out how to separate out your model, view, and controller elements from a Swing application, then you know how frustrating it is to understand all of the examples you see that use complex listeners and/or overloaded or overridden Swing components.

I'd like to discuss an easier way to do all of this, as long as you understand two things. First, you have to know how the Swing components are supposed to be layered and how they use their listener events. Second, you have to understand the MVC model so you'll be able to see how all of this is going to get glued together.

This article assumes you understand at least this much. If you don't, then this will probably offer no more help to you than someone else's example. Buyer beware...

I'll present the development of this example in a staged approach similar to the Agile methodology of 'Test Driven Development' or 'Test First'. I'm not going to write tests; just what the code would be that would fire the need to write a test to get to the next step so that as pieces are needed they will only then be introduced.


Background
I discovered the pattern that I use here in a complex Swing application that I wrote, a simulated rules based foreign currency trading application that requires some pretty fancy graphing on a Swing Panel object. Unfortunately, I came to the pattern long into the development of the application.

When I started coding the application, my intent was to just create a simple one shot screen to display simulated prices from an algorithm that I wrote. I created a pojo, instantiated my Swing components, pumped my pricing and graphing algorithms at the Panel, and figured I was done. I needed something simple because my Swing development at that time was really cursory.

But major scope creep over the years has ballooned the application to almost two dozen subdirectories of classes that do all sorts of things. The one thing that never changed was how I was communicating with my view objects, and that way is too tightly coupled for my liking.

It works, and it's close to being 'okay', but it's just not really MVC compliant. The thing is, I knew how to layer out things in a web application; I've been doing J2EE web applications development since 2001. But I didn't understand how to configure a Swing application correctly, and at the time I was having a tough enough time just building the Swing components and figuring out how the component ActionListener stuff was supposed to work that I just failed to compose the overall application correctly.

So, I thought I'd do a little research into this and see what I could learn. But as I've stated, reading some of this stuff with all of the creation and registration of listeners and all that was just making my hair hurt. I figured there had to be an easier way. This article is what I found out. It is a very simple example showing a very simple approach to one way to accomplish somewhat of an MVC structure in a Swing application.


The Controller As A Mediator
In the classic Swing architecture, events on the model fire to the view. The View then notifies the controller and the controller does its thing. This allows many different views to subscribe to the same model, which then becomes agnostic to the model; it just reacts to the model changes.

That's all well and good, but it does imply the need for the developer to create a more difficult communication process through all of the creation and registration of specialized listener objects. However, in my haze of trying to understand all of that, I found that if I stuck the controller between the model and the view to act as a mediator, my design was much simplified. I could actually understand it. So, I thought I'd offer it up as a possible solution. Now, it's not the only solution and maybe it's not the approved or 'best practices' solution. But it's easy to understand and implement.

The Mediator Design Pattern simply implies that the controller is going to buffer what's going on in both the model and in the view. To do this, the controller will either have to keep an instance of the model on it (my approach because it simplifies the design) or the model would be passed through the system similar to how you would experience the HttpServletRequest and HttpServletResponse objects in a web application. To do the latter, you might create some sort of Map object that would contain request and response type objects and just pass this around; the Map can then have anything in it and you don't have to worry about bloated parameter lists that have to change every time you determine a new object is needed downstream somewhere.

The controller is also responsible for instantiating the view object and kicking that display off. When this is done, a copy of itself is passed in the constructor of the view as well as the instance of the current model. This means that both the controller and the model become instance properties on the view. Here's the first run at putting the controller together:


package com.pmr.mvc;
import java.util.Date;

public class MyController {
    private MyModel model = null;

    public void processRequest() {
        model = new MyModel();
        MyView view = new MyView(this, model);
        view.display();
    }

    public static void main(String[] args) {
        MyController mcon = new MyController();
        mcon.processRequest();
    }
}

Pretty straight forward stuff. I create an instance of the MyView view object, passing it the controller and model instances, and then tell the view object to display itself. Before we get to the view, though, let's take a look at the business case for this example through the model object that I'll be using.


The Model Class
Let's say the purpose of my application is to enable a user to send from the view screen the current time (as a java.util.Date object) to the controller. Then, I want to have the controller update the model with that start time and create a new time, the reply time, that it takes to make the communication.

The controller updates the model with this new time and sends it back to the view for display. Pretty simple, so here's what our model looks like:


package com.pmr.mvc;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

public class MyModel {
    List startDates = null;
    List replyDates = null;

    public MyModel() {
        startDates = new ArrayList();
        replyDates = new ArrayList();
    }

    /** Set by the MyView.java object */
    public void setStartDate(Date astartDate) {
        startDates.add(astartDate);
    }

    /** Set by the MyController.java object */
    public void setReplyDate(Date areplyDate) {
        replyDates.add(areplyDate);
    }

    public List getStartDates() {
        return startDates;
    }
    public List getReplyDates() {
        return replyDates;
    }
    public void show() {
        System.out.println("\r\n");
        for(int i=0; i < startDates.size(); i++) {
            System.out.println(
                startDates.get(i).toString() + " - "
                + replyDates.get(i).toString());
        }
    }
}

As you can see, the model is going to keep track of all of the user's requests of start dates in the startDates List and the controller's reply dates in the replyDates List.

The java docs on the model methods clearly show their intent: the view object is to call the setStartDate() method with the current time, and the controller is to call the setReplyDate() method with its calculated time.

So, somehow, the model has to be sent to the controller where the controller can calculate the reply date and set it into the model via the setReplyDate() method. And then the model has to get passed back to the view so the view can get updated. Whew!


The View Class
Okay, now it's time to see the view class. First, I don't extend JFrame or JPanel or anything for the view. The Swing components are created as instance propeties on the view object, which, sure, sorta-kinda makes the view a controller in its own right. But believe me, it's a lot easier to understand and control these objects in this manner - and it separates the controller from knowing that it has to build a JFrame or panel or whatever; it lets the view object worry about all of that. Let's take a look:


package com.pmr.mvc;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Date;
import java.util.List;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;

public class MyView {
    private MyController myCon = null;
    private MyModel model = null;
    private JFrame dFrame = null;
    private JPanel resultPanel = null;
    private JLabel resultLabel = null;
    private int pWidth = 300;
    private int pHeight = 150;

    public MyView(MyController acon, MyModel amodel) {
        myCon = acon;
        model = amodel;
    }

    public void display() {
        JFrame.setDefaultLookAndFeelDecorated(true);
        dFrame = new JFrame("MVC Swing Communication Example");
        dFrame.setBackground(Color.darkGray);

        resultPanel = new JPanel();
        resultPanel.setBackground(Color.gray);
        resultPanel.setSize(new Dimension(pWidth, pHeight));
        resultPanel.setFocusable(true);

        JButton updateBtn = new JButton("Update");
        updateBtn.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                model.setStartDate(new Date());
                model = myCon.update(model);
                model.show();
                String lastDt = buildReplyDateStr(model);
                resultLabel.setText(lastDt);
            }
        } );
        resultPanel.add(updateBtn);

        String startMsg = "Starting Message";
        resultLabel = new JLabel();
        resultLabel.setText(startMsg);
        resultPanel.add(resultLabel);

        JPanel expandedPanelBase = new JPanel(new BorderLayout());
        expandedPanelBase.add(BorderLayout.CENTER, resultPanel);
        dFrame.getContentPane().add(resultPanel);

        // Display the window.
        dFrame.pack();
        dFrame.setBounds(200, 200, pWidth + 20, pHeight + 20);
        dFrame.setVisible(true);
    }

    /** Get reply date just created by the controller */
    protected String buildReplyDateStr(MyModel amodel) {
        List replyList = (List)amodel.getReplyDates();
        Date lastDate = (Date)(replyList.get(replyList.size()-1));
        return lastDate.toString();
    }
}




The Glue That Holds It All Together
Whoa! That's all just clutter to build the view. Here's the key code in the view to focus on:


        JButton updateBtn = new JButton("Update");
        updateBtn.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                model.setStartDate(new Date());
                model = myCon.update(model);
                model.show();
                String lastDt = buildReplyDateStr(model);
                resultLabel.setText(lastDt);
            }
        } );

Because the model has been passed into the view object, the view can change it. The view is supposed to change it; the user is going to hit the 'Update' button to set the start time in the model, and then expect that a new date will be returned.

It's easy to update the model because it was passed in via the constructor. The communication problem here is how to then pass this information back to the controller so that the date can be updated. Remember, the business of creating that new time is that of the controller and not the view or the model. This is the point of the whole problem the article is addressing, and which I did so poorly in my initial application development of the project.

In a classically designed Swing application, this is where you'd get into all of the overhead (and confusion for me) of creating, registering, and calling specialized listener objects. But why bother with all of that when I have an instance of the controller at the ready?

All I have to do is update the model and then pass it back to the controller. The controller will then do its thing, update the model with the reply date, and then send the model back to the view. This is all done via what we term a 'call-back' method. This is because the method 'calls back' to the object (the controller) it needs access to via the instance property it holds of that controller. It's all done in the simple statement:


                model = myCon.update(model);




Completing The Controller
So, now all we have to do is write the update() call-back method on the controller like this:


    public MyModel update(MyModel amodel) {
        model = amodel;
        //pause for a breath...
        for(int i=0; i<400000000; i++) {}
        Date replyDate = new Date();
        model.setReplyDate(replyDate);
        return model;
    }

Let's step through this because it's the crux of the whole pattern. First, note that the model that the view is passing into the method is different than the model instance on the controller, i.e. the view stuffed the start date into the startDates List so that List is now different than the one on the controller. We thus update the controller's copy of the model with: model = amodel;

Next, because I want to keep this example really simple, I create a new date a second or so different than the passed in date and put this into the model.

By doing this, the model has changed and so I need to pass it back to the view so the view can display it. Simple.


Simplicity Hides Potential Complexity
But this simplicity is deceiving. Real world applications are not this easy. In my foreign currency graphing application, for example, a similar call-back to my controller object is going to create a veritable storm of activity starting with the instantiation several DAO type objects which will be parameters along with my model to the instantiation of a service object.

This service object, a layer abstraction from the controller, acts as an agent class that goes all over the place: it instantiates a spider object that opens a socket to a website, sucks back the page, and passes the page to a parser object that pulls the data out into an object graph.

The object graph is then sent off to another service object that scrubs the data for inconsistencies, and then passes the object graph to one of the DAOs to populate the database.

Once that is done, control goes back to the controller that creates another DAO and service object that pulls the data out of the database, sends it to another service object that calculates pricing metrics, moving average lines, and other statistics of that data, and then packages it all up into a new model object which, TA-DA!, finds its way back to the controller where it is piped back to the view object for display.


Conclusion
Regardless of the complexity that may be necessary in a real world application, the simple process that I have laid out is really all there is to it. The pattern has three components or structuring rules:

  • Make the controller a mediator.
  • Pass instances of the controller and model to the view.
  • Create a call-back method on the controller.

Back to Thoughts On Technology Page | Back to Peter's Home Page | Back to Family Home Page