CISC 3115

Exercise 4: Model-View-Controller

Objectives:

Overview

[Note: this is an extended version of the introductory materials I asked you to read before class. I've highlighted the tasks I'm asking you to in this face, but there's a little more reading to do around them, as well.]

In this exercise, we're going to focus on the Model-View-Controller design pattern we talked about briefly in class. There are two reasons for this: (1) MVC is a very commonly used design pattern, across many languages and application contexts; (2) MVC is (I think) a helpful way of thinking concretely about design, about how you can create effective relationships among objects in your applications.

This exercise has two parts. First, we'll look at another version of the "SwingLine" code that we looked at during the application activities, and we'll see how the code can be "refactored" to make it even more MVC-ish. Then, we'll look at how Swing itself uses MVC to design many of the Swing components. This will involve a fair amount of API-reading, and not so much code-writing... but that's actually how things go a lot of the time: you have to read a fair amount before you figure out how to write a couple lines of code.

MVC Intro/Review

I talked for a few minutes in class about Model-View-Controller. That was no accident. While there are many design patterns floating around Java, MVC shows up a lot in Swing. And beginning to think in terms of design patterns is an important step in your development as a software developer. (For example, there are a ton of books written about design patterns, many focusing on a particular programming language.)

As we saw in the big-small-circle application, MVC is often used to structure the user interface of an application; it's a useful way make sure we're keeping the three kinds of behavior (keeping track of data [the model], displaying the data [the view], and letting the user change the data [the controller]) fairly separate from each other (and therefore making sure that we're not programming like that jerk Larry).

SwingLine MVC

Here is the source code for a "fully" Model-View-Controller version of the SwingLine application we also looked at last week. Take a look. The big difference between last week's version and this week's version is that I put the "application data" (in this case, the length and thickness of the line) into its own class, which I called LineModel. Most of the behavior in this class is essentially getter and setter behavior. But there is some additional code relating to the fact that this is part of an MVC design: when the data in the model changes, other objects (especially the View) may want to know about the change so they can update in response. So, I wrote LineModel to extend Observable. The Observable class offers the basic behavior of allowing other objects to "register" as "listeners" (similar to buttons and ActionListenerss). So a class that extends Observable gets all that behavior automatically. You'll also see that the main application class implements the Observer interface, which includes a single method, update(). My definition of the update() method tells the various elements of my View to update themselves with the new data from the Model. Read the documentation for Observable and Observer—you'll see that these have been deprecated because they're not as flexible and powerful and modern applications require, but they're still very good for exploring the foundational concepts.

Here are two small tasks for you to do with this code:

  1. The comments note that things would be a little easier if LineModel offered methods to increment and decrement the thickness. Add those methods, and update the rest of the code to use those methods. Hint: these new methods should do very little work—they should re-use code you've already written, i.e. they should call existing methods whenever possible.
  2. Add a "Reset All" button that appears to the right of the lengthDisplay TextField. This button should set the length to 100 and the thickness to 1. Pay attention to what code you need to write and what code you don't need to write. (And as above, re-use as much code as you can.) While you're at it, maybe you should declare 100 and 1 as constants in the LineModel class.

Swing and MVC

Let's switch gears a bit and look at Swing components themselves. For example, what do we know about the JTextField class. Which of the following are responsibilities/behaviors of this class?

  1. Store some text data
  2. Display some text data
  3. Respond to some keyboard entries

While you're thinking about that, look at the JList discussion on p. 417 of the book. Which of these are responsibilities of this class?

  1. Store a list of objects
  2. Remember which objects have been "selected"
  3. Display the values of the objects in the list
  4. Respond to mouse-clicks
Actually, most of the Swing UI components have these responsibilities: storing some data, presenting that data, and allowing users to interact with that data. (OK, a JButton doesn't have that much going on—but that's one of the exceptions.) By the way, for those two multiple choice questions, all of the listed items are correct. In preparation for this week's exercise read this short excerpt that discusses MVC generally as well as its application within Swing. Also, read the introductory text in the documentation for JList, paying particular attention to the discussions about ListModel, ListSelectionModel, and how cells are "painted" (i.e. the View).

Introducing the Currency class

In Project 2, we'll use these ideas to assemble a slightly complex Swing application. But for this exercise, we'll focus on one non-Swing class (Currency) and the JList component. First, Currency. As the docs say, this is a class whose instances represent currencies, using the ISO 4217 standard. This is an international standard; the Wikipedia page for the standard includes a table of active codes—basically, this is the standard that assigns three-letter codes, like USD for the United States dollar. If you ever exchange money, you see these codes in use. And software applications that deal with currency exchange also use these codes (as well as some related standards).

Customizing a JList

But for now, let's work on making a list of Currencys available to our users. Take a look at this code. It declares an array of Currency objects, then creates a JList<Currency> that holds those objects (note the JList constructor that lets me create the list from an array of objects). Maybe you want to check the wikipedia page about currency codes to find out what those codes represent. Notice, too: this is not a JList of Strings, and I haven't given any instructions about how the JList is supposed to display each of the currencies in the list. What will happen? Make a hypothesis, then run the code and find out. Was your hypothesis correct? Why does the program have this behavior?

Maybe you don't think this behavior is the best; I certainly don't. How can we address this? Well, remember that one of the responsibilities of JList is to display its contents. If we consult the documentation, we learn (several paragraphs down) that "Painting of cells in a JList is handled by a delegate called a cell renderer." That is, every JList object has an instance variable that refers to an object implementing the ListCellRenderer interface. And, importantly, we can tell the JList to use a different cell renderer by passing a reference (to the cell renderer we want) to the setCellRenderer() method of JList.

So, if we want to customize how our list items are displayed, we just need to write our own class implementing that interface. Note that the interface only includes one method, getListCellRendererComponent(), but it looks like it has to do a lot of work. We don't want to do that much work!—we just want to tweak the "default behavior" a little bit. This sounds a little bit like the situation with the paintComponent() method. The "default painting behavior" of a JPanel is not helpful to us (it basically does nothing), but the paintComponent() method does a lot of work that we actually can't do ourselves. The solution there was to start with the JPanel class and then override the default behavior of paintComponent() with our own behavior. This raises a question: is there a class out there that does the "default cell renderer" behavior? If you look closely at the JList documentation, you might find something that says "See also: .... DefaultListCellRenderer." Hmmm interesting! Look at that documentation: not only does it implement the ListCellRenderer interface, but it also extends JLabel! That is, by default, a JList is displayed with a bunch of JLabels!

But the real point is that, just as we can change how a JPanel is painted by overriding the paintComponent() method, we can change how JListcells are rendered just by subclassing the default renderer. So, this is your first task (after all this reading): modify the CurrencyList code to display each Currency in a more user-friendly way.

A few notes about this, though:

Another JList customization

OK, let's do something crazy... let's let the user add currencies to this list. According to the Currency API, we can create Currency objects using the getInstance() method, which takes a three-letter currency code as a parameter. So, let's add to the user interface— put in a JTextField where a user can enter a currency code, and a button labeled "Add" that the user can click to add the language to the list of currencies. (While you're at it, you might want to put a few more JLabels in so the user can tell what she's looking at.) To make this work, the main thing we need to do is implement the button listener. There's two issues here...

First: how do we add something to a JList? There are a lot of methods in the API, but none seem to involve changing the contents of the list. But wait! A JList is an MVC-style component (which element of MVC did we fiddle with above?)... and if we're talking about the contents of the list, it sounds like we're talking about the model. And actually, yeah, this is the first thing discussed in the API documentation. The third sentence says something about "the JList constructor that automatically builds a read-only ListModel instance for you." Uh-oh. Read-only? That sounds like I can look at the contents, but I can't change them. I don't want a read-only ListModel. But hang on—what is this ListModel? Hmmm, let's keep reading. Doo-de-doo... ah! "Simple dynamic-content JList applications can use the DefaultListModel class to maintain list elements." That sounds promising... so pursue that idea in order to complete your button's ActionListener.

The second issue has to do with input validation: what if the user enters an invalid currency code? The getInstance() method says it will "throw an IllegalArgumentException" if it doesn't get a supported currency code, but we haven't studied exceptions yet. We can also check ourselves: the static method Currency.getAvailableCurrencies() will return a collection (a Set, specifically) of supported currencies, and we can use Set methods to see if the user's input is valid. (And let's be subtle: if it's a legal code, then when the button is clicked, a new currency is added to the JList and the text field is cleared. If the currency code is not legal, then nothing happens to the JList and nothing happens to the JTextField either.