Thursday, June 4, 2009

Objects in Java

Chapter 5

In this chapter, we get to the heart of Java and explore the object-oriented aspects of the language. The term object-oriented design refers to the art of decomposing an application into some number of objects, self-contained application components that work together. The goal is to break your problem down into a number of smaller problems that are simpler and easier to handle and maintain. Object-based designs have proven themselves over the years, and object-based languages such as Java provide a strong foundation for writing very small to very large applications. Java was designed from the ground up to be an object-oriented language, and all the Java APIs and libraries are built around solid object-based design patterns.

An object design "methodology" is a system or a set of rules created to help you break down your application into objects. Often this means mapping real-world entities and concepts (sometimes called the "problem domain") into application components. Various methodologies attempt to help you factor your application into a good set of reusable objects. This is good in principle, but the problem is that good object-oriented design is still more art than science. While you can learn from the various off-the-shelf design methodologies, none of them will help you in all situations. The truth is that there is no substitute for experience.

We won't try to push you into a particular methodology here; there are shelves full of books to do that.[1]

Instead, we'll just provide some common sense hints to get you started. The following general design guidelines will hopefully make more sense after you've read this and the next chapter.

  • Hide as much of your implementation as possible. Never expose more of the internals of an object than you have to. This is key to building maintainable, reusable code. Avoid public variables in your objects, with the possible exception of constants. Instead define accessor methods to set and return values (even if they are simple types). Later, when you need to, you'll be able to modify and extend the behavior of your objects without breaking other classes that rely on them.

  • Specialize objects only when you have to—use composition instead of inheritance. When you use an object in its existing form, as a piece of a new object, you are composing objects. When you change or refine the behavior of an object (by subclassing), you are using inheritance. You should try to reuse objects by composition rather than inheritance whenever possible, because when you compose objects, you are taking full advantage of existing tools. Inheritance involves breaking down the barrier of an object and should be done only when there's a real advantage. Ask yourself if you really need to inherit the whole public interface of an object (do you want to be a "kind" of that object) or if you can just delegate certain jobs to the object and use it by composition.

  • Minimize relationships between objects and try to organize related objects in packages. Objects that work closely together can be grouped using Java packages, which can hide those that are not of general interest. Think about how much work it would take to make your objects generally useful, outside of your current application. You may save yourself a lot of time later.

Chapter 5

Classes

Classes are the building blocks of a Java application. A class can contain methods (functions), variables, initialization code, and, as we'll discuss later on, even other classes. It serves as a blueprint for making class instances, which are runtime objects that implement the class structure. You declare a class with the class keyword. Methods and variables of the class appear inside the braces of the class declaration:

class Pendulum { 

float mass;
float length = 1.0;
int cycles;

float getPosition ( float time ) {
...
}
...
}

The Pendulum class contains three variables: mass, length, and cycles. It also defines a method called getPosition(), which takes a float value as an argument and returns a float value as a result. Variables and method declarations can appear in any order, but variable initializers can't make "forward references" to other variables that appear later. Once we've defined the Pendulum class, we can create a Pendulum object (an instance of that class) as follows:

Pendulum p; 

p = new Pendulum( );

Recall that our declaration of the variable p doesn't create a Pendulum object; it simply creates a variable that refers to an object of type Pendulum. We still have to create the object, using the new keyword. Now that we've created a Pendulum object, we can access its variables and methods, as we've already seen many times:

p.mass = 5.0; 

float pos = p.getPosition( 1.0 );

Two kinds of variables can be defined in a class: instance variables and static variables . Every object instance has its own set of instance variables; the values of these variables in one object can differ from the values in another object. We'll talk about static variables later, which, in contrast, are shared among all instances of an object. In either case, if you don't initialize a variable when you declare it, it's given a default value appropriate for its type (null, zero, or false).

Figure 5-1 shows a hypothetical TextBook application that uses two instances of Pendulum through the reference-type variables bigPendulum and smallPendulum. Each of these Pendulum objects has its own copy of mass, length, and cycles. As with variables, methods defined in a class may be instance methods or static methods. An instance method is associated with an instance of the class, but the relationship isn't quite as simple as for variables. Instance methods are accessed through an object instance, but the object doesn't have its own copy of the methods (there is no duplication of code). Instead, the association means that instance methods can "see" and operate on the values of the instance variables of the object. As you'll see in Chapter 6 when we talk about subclassing; there's more to learn about how methods see variables. In that chapter we'll also discuss how instance methods can be "overridden" in child classes—a very important feature of object-oriented design. Both aspect differ from static methods, which we'll see later are really more like global functions—associated with a class by name only.

Figure 5-1. Instances of the Pendulum class

figs/LJ2.0501.gif

No comments:

Post a Comment