Previous Lesson: Text Field with String Model
Next Lesson: Lists
Introduction | ||
In the last lesson, we saw how we could interact directly with a model (which was a String) and not have to worry about how it is displayed in AWT components. This lesson, we'll see how we can do the same thing with models that aren't Strings: things like integers, dollars, points, employees, and basketball players. | In Smalltalk, input fields handle a lot of this stuff natively, as we'll see throughout this lesson. | |
The PrintConverter | ||
So if text fields have to deal with Strings and we want to
deal with some other object, how do we convert between them? That's the
job of a PrintConverter.
PrintConverter is actually an interface which defines two methods:
|
Starting with VisualWorks 2.0, Smalltalk also has a class which fills this role called PrintConverter. The methods are actually called #toPrint: and #toRead: .
| |
I can hear some of you in the back saying: "Java already does this: Object has a toString() method that anyone can override. Why duplicate that?" Well, toString() is really more of a debugging method and suffers from a number of limitations, the greatest being that an object can only represent itself in one way. If I have a float, how can I alter the precision in which it displays itself? By encapsulating the behavior into a separate service object, we open the doors to a wide range of custom representations. | ||
Example 4: Using an MVCTextField with a float | ||
Okay, this example is actually in three parts, each progressively easier. We're going to start with the most explicit method of associating a float with a text field, but then we'll start to use more of the capabilties of MVCTextField. Sounds like fun, huh? In each iteration we're trying to do the same thing: have a float model and to be able to display and change the model with a text field. Each example will share the following features:
update() method is called whenever the model changes. Very useful.Enough talking - open up that Java Console and let's go! | The Smalltalk version of this example (only one iteration, not three) can be found in Example4.st and can be started by evaluating "Example4 open". | |
Iteration 1The running applet is displayed below. Play around with it so you're familiar with the functionality. Since all the iterations should act exactly alike, you're really going to want to look at the source code to see what'd going on. | ||
Example 4-1 | ||
Here are some comments on this example:
| ||
Iteration 2Okay, this should really be called iteration 1.5 - there is only one line of code that changed from the first iteration. If you look at the source code, you can see that we don't explicitly create a PrintConverter. Instead, we tell the field that it has a type ofTYPE_FLOAT ,
which will automatically create the appropriate PrintConverter for us. Check
out the setType()
method for the other built-in types. Note that while we gain a little convenience,
we lose some flexibility in being able to choose the exact PrintConverter
to use.
| ||
Example 4-2 | ||
Iteration 3The one big wart in both of the previous iterations has been the funny business with the float. Since ValueModel only knows how to deal with Objects, we keep having to create temporary Float objects and do some casts, which we'd like to avoid if we could. None of this is really a show-stopper - it's just kind of inconvenient.While we can't change the fact that ValueModels have to deal with Objects, we can use a special kind of ValueModel which presents a much more attractive interface for dealing with base numbers: the NumberValueHolder. Check out how much more straight-forward this source code is. | ||
Example 4-3 | ||
Chaining ValueModels | ||
One thing that we have found to be useful is "chaining" together models, or having one model adapt another. You could draw a loose analogy with Streams: there are Streams and there are StreamFilters, and you can hook them together to get all sorts of interesting behavior. We can do the same thing with models. The abstract class that helps us do it is ValueAdaptor. | In Smalltalk, you can do this with PluggableAdaptors or by using a subjectChannel with your protocol adaptors (like AspectAdaptor). | |
Example 5: Using an MVCTextField with a ValueAdaptor | ||
This example will show how to chain these models together in a very basic fashion. You can imagine extrapolating this to a complex network of models if your application demands it. Here, we're going to create a ValueAdaptor called a TimesTwoValueAdaptor. Basically, you give a TimesTwoValueAdaptor a model to "adapt to" and its value always appears to be twice that of the "reference" model. So we'll toss up one text field that displays the reference model, and another (read-only) text field that displays the ValueAdaptor. As always, check out the source code and follow the comments to get the inside scoop. | The Smalltalk version of this example can be found in Example5.st and can be started by evaluating "Example5 open". | |
Example 5 | ||
Moving on... | ||
That's pretty much the scoop with text fields. You should now be able to use MVCTextField to deal with all different kinds of objects without having to micro-manage the view of those objects. But there's more to AWT than text fields. Next lesson we'll tackle the (IMHO) most annoying component, the List. |