CSE190L Notes for Wednesday, 4/25/07

I spent time discussing the Model/View/Controller design pattern in more detail. I began by talking about a classic problem for programmers that can be described as the update problem. For example, suppose that you have a spreadsheet program like Excel that allows you to enter data and formulas into individual cells and also allows you to make charts that display the data. What happens if the user changes some of the data or the formulas? Shouldn't the chart be updated as well?

In the original version of Excel, this didn't work properly. You would have to generate the chart a second time to get the changes to the data to be included in the chart. But this was fairly quickly fixed. Now you can have a spreadsheet with several charts and an update to the underlying data updates everything.

So how is it done? We talked about this for a while and people were basically suggesting that there be some kind of central spreadsheet object that would handle lots of this work. I pointed out that this is the kind of solution that procedural programmers come up with. In procedural programming, you're used to central control with a method main that runs everything. The challenge in object oriented systems is to spread out the responsibility across a large number of objects.

I mentioned that I learned a lot about this from a book by Arthur Riel called Object-Oriented Design Heuristics It's a great book, although the examples are all in C++. I've included a link to this book under "useful links" along with a link to a summary of his 60 heuristics from the book. His heuristic 13 is:

Do not create god classes or god objects in your system. Be very suspicious of a class whose name contains DRIVER, MANGER, SYSTEM, SUBSYSTEM, etc.
The spreadsheet class we were ending up with would have been a god class. Instead, as he says in heuristic 12, you want to:

Distribute system intelligence horizontally as uniformly as possible, that is the top level classes in a design should share the work uniformly.
So the challenge is to find a way to divide up the high level work into different aspects that can be handled by different objects.

The first such division is to separate the model from its views. In doing so, we are following another design pattern known as the observer pattern, which was one of the 23 patterns included in the original GOF design patterns book.

I told people that I would be using a simple program I call "clicker" as an example. It allows the user and the computer to alternate clicking on various locations on the screen, and it draws red circles where the computer has clicked and blue circles where the human has clicked. The code for this is all in handout #12.

In the case of the clicker program, the model keeps track of the locations where the two players have clicked. The view is the panel that displays a graphical representation of the red and blue circles. For a program this simple, it probably is overkill to do the kind of model/view separation that we're exploring, but it allows us to focus on how to implement the design pattern.

So how do we establish communication between the model and the view? And how do we make it possible to have the model talk to multiple views? We introduce the idea that the model has various "listeners" that want to be informed when an update occurs. Each listener is registered with the model and it contacts its listeners whenever there is an update. The listeners in turn then talk to the model to perform whatever action is appropriate. For a view, this would involve potentially redrawing the image because the data in the underlying model has changed.

We discussed how this communication could be established. Do we have some kind of AbstractListener class that each view implements? Do we assume that all listeners will be JPanels? Neither approach works well. A better approach is to introduce an interface that specifies how the model will contact its listeners and then have each listener implement the interface. Depending upon the specific application, you might have several methods that allow you to describe different kinds of updates, but for our purpose, we included just a single method that said that an update had occurred:

        public interface ClickerListener {
            public void update(ClickerModel model);
        }
The model sends a reference to itself in the call in case the view wants to talk to the model. This is known as a callback protocol ("call me back if you want more info about the update").

Java has built-in support for the observer pattern. There is a class called Observer and an interface called Observable. I personally don't like using these because they do so little for me that I'd rather just do it myself. You just need to keep track of a list of listeners in the model and then have a method for registering a new listener and a method that calls each of the listeners.

The sample program also separates the user interface code into a separate controller class. With this third component, we get to the MVC pattern. Almost everyone agrees that model/view separation is very important in large interactive programs. The separation of the controller from the view code is more controversial. But in the MVC design pattern, we try to separate the program into these three major components and the sample program includes this separation.

We also ended up discussing an enum type for distinguishing between a computer player and a human player. The model class includes the following enum:

        public enum Player {COMPUTER, HUMAN};
This completed our discussion of the overall program structure. I said that I also wanted to discuss some subtle issues about swing. When we ran the program, we found that it didn't behave very well. We wanted it to seem like the human was clicking and then the computer was clicking, but instead it seemed like they were both clicking at the same time. I said that we'd discuss this in Friday's lecture.


Stuart Reges
Last modified: Fri May 4 10:49:10 PDT 2007