Lesson 9.1 Combining Objects
Up until now you've been building applets using predefined "parts": Label objects, Button objects, and so forth. In this lesson, you're going to learn how to create new components and then use your new components to build larger applications.
When you create new, reusable components, there are two general strategies you can use.
- You can combine different fundamental parts together to create a more complex component. That is what you'll learn to do in this lesson.
- You can extend a more basic component by adding new features or behaviors. You'll learn how to do that in the next lesson, "Inheritance"
These two different strategies are used to express two different kinds of class relationships:
- The HASA relationship when one class is a composite of other objects.
- The ISA relationship, when one class is an extension of another class.
Composite Objects
Inheritance, which you first met in Lesson 2.2, is a simple and powerful way to reuse and extend existing software components. As you'll see in the next lesson, inheritance lets you define your own version of existing classes, customized for your special needs.
Suppose that your corporate policy requires that all applications use 14-point TextFields and 18-point bold Labels. Using inheritance, you can simply create a pair of new classes by extending the TextField and Label classes, and avoid the extra work of sending the setFont() message to every TextField and Label object in your entire application.
Inheritance isn't the right solution for every situation, however. What if you need a component that combines both a Label and a TextField object? Inheritance doesn't help in this situation. If you try to write:
public class LabeledTextField
extends Label, TextField
{
} |
Java goes ballistic. It won't even compile your code. Java's hard-and-fast rule is that each class can extend only a single superclass.
Fortunately, there is a simple solution. You can bundle both your Label and your TextField together in a single class, making each of them fields. This technique is called composition.
The idea of a composition relationship is really quite simple: In the composition relationship, an object is composed of other objects, which are its "parts." That's why it's called a HASA relationship, because we say:
- A car has a motor, or
- A bicycle has a seat, or
- A computer has a CPU
You've already used composition, when building applets that contain both Labels and TextFields. These components are part of the applet; they "live" only as long as the applet lives, and they exist only to serve the purposes of the applet.
The LTextField Class
For the rest of this lesson, we're going to use composition to build a labeled text field class in several stages. Our first few attempts won't be very impressive, but stick with it, and you'll be rewarded.
By themselves, text fields are not very useful. The purpose of a text field is to get input from your users. But, without a label, your users don't know what information to put into each field. If you look at any "business style" application, almost every text field has a label attached to it.
Let's see if we can combine two of Java's built-in AWT classes, the TextField class and the Label class, to create a new-labeled text field. For right now, we'll use an applet to "hold" the parts together.
As we work through the example, it will help if you are able to follow along on your own computer.
The LTextField1 Class
Here are the steps to follow:
- Create a new applet named LTextField1. Remember that this must be in a file named LTextField1.java, and contain a single public class named LTextField1 that extends the Applet class.
- Remember to import the java.awt package and the java.applet package.
- Add two fields: a Label field called theLabel, and a TextField called theText. You don't have to initialize either field.
- Add an init() method to initialize both of your fields:
- Initialize your Label to say "Name:". Make the Label right aligned.
- Initialize your TextField with a width of 40.
- Add both fields to your applet, using the add() method
- Create LTextField1.html. Set HEIGHT to 50 and WIDTH to 400.
When you're finished, compile and run your program in appletviewer. As you can see, it looks pretty good:
What happens, though, if you try to resize the applet? Oops! As you can see, when added as individual components to an applet, the Label and the TextField really don't act like pieces of a single, cohesive whole.
Using LTextField1 Objects
Since LTextField1 is an applet, it's common for us to think of it as a "program." But, Java really doesn't have "programs" like other programming languages. All Java has is classes and objects.
When you load LTextField1.html into appletviewer, what appletviewer does is to create an LTextField1 object, and then call its init() method. If the appletviewer program can create an LTextField1 object and start it running, perhaps other classes could contain LTextField1 objects as well.
Let's see by creating an applet that uses LTextField1 objects. As with the previous section, you'll learn more if you follow along instead of just reading.
Here are the steps you should follow:
- Create a program that will use LTextField1 objects. Call the applet User1, and extend Applet. Make sure User1.java is in the same directory as LTextField1.class
- Add two LTextField1 objects as fields inside the User1 class. You can name them first and second. Initialize them "in place" using new LTextField1().
- Add an init() method to User1. Inside your init() method use add() to put both first and second on to the surface of the applet.
- Create the User1.html file that loads User1. Set the HEIGHT to 200 and the WIDTH to 400.
Compile User1.java and then run User1.html in appletviewer. What do you see? Does this look familiar?
Fixing User1
If you read back over the description of how appletviewer or your Web browser runs your applet, you might notice that the User1 class is missing one of the steps. Although appletviewer is sending the init() message to the User1 object, the User1 object is failing to send the init() message to its two LTextField objects.
Remedy that by adding the following two lines to the User1 init() method:
public void init()
{
first.init();
second.init();
add(first);
add(second);
} |
Recompile User1.java, and then rerun User1.html in appletviewer. You should see something that looks like this. Go ahead and resize it to your heart's content.
It sure looks like the Label and TextField objects that make up an LTextField object are bound together to make up a new component.
What Have You Learned?
You should now be convinced that applets are objects, just like Button objects or Label objects. You should also be convinced that, if you have an object, you can embed that object as a field inside a class.
As far as the LTextField1 class itself, it's really not all that useful. Here are just a few of its shortcomings:
- Because the browser only calls the init() method on the applet it loads, when you use an LTextField1 object, you have to send it the init() message. Other object fields don't require this, so why should the LTextField1 class?
- The LTextField1 class is not customizable. Every LTextField1 object is always labeled "Name." That's like having a Label class that always says "Hi Mom." It's occassionally useful, but lacks universal applicability.
- The LTextField1 class can't do much. It doesn't repsond to many messages. You can't, for instance, change the text by sending one of your LTextField1 objects the getText() or setText() message.
Improving LTextField1
The best way to fix LTextField1 is to "design it by using it." Ask yourself the question "How would I use an ideal LTextField object?" Your answers will give you some ideas about how the LTextField class should work.
Here are the answers that I came up with. I want to be able to:
- Specify both the text to be used in the Label part and a width to be used for the TextField part, like this:
| LTextField name = new LTextField("Name:", 30); |
- Have a built-in or default value for the width, so I wouldn't have to specify it if I only wanted to specify the Label portion like this:
| LTextField ssn = new LTextField("SocSec#"); |
- Finally, specify just the width of the TextField portion, and have the Label portion default to an empty Label like this:
| LTextField addr2 = new LTextField(35); |
[Of course, I could just use a TextField in this case, but then I'd lose any of the other great improvements I plan on adding to the LTextField class].
To add this functionality, we need to add some new methods to the LTextField1 class. These new methods are called constructors. You'll learn how to build a basic constructor in the next lesson.
You can download LTextField1.java and User1.java here
Something to Talk About
Think of the built-in components that we've already used. Can you describe two new components that you could create using the built-in components and composition?
Please continue to the next section of this lesson.
|