Section 4.5
Object-Oriented Programming


THERE ARE SEVERAL WAYS in which object-oriented concepts can be applied to the process of designing and writing programs. The broadest of these is object-oriented analysis and design which applies an object-oriented methodology to the earliest stages of program development, during which the overall design of a program is created. For help in writing programs, OOP makes it possible to produce generalized software components that can be used in a wide variety of programming projects. More generally, inheritance makes it easier for programmers to reuse old work by building on existing classes.


Object-oriented Analysis and Design

A large programming project goes through a number of stages, starting with specification of the problem to be solved, followed by analysis of the problem and design of a program to solve it. Then comes coding, in which the program's design is expressed in some actual programming language. This is followed by testing and debugging of the program. After that comes a long period of maintenance, which means fixing any new problems that are found in the program and modifying it to adapt it to changing requirements. Together, these stages form what is called the software life cycle. (In the real world, the ideal of consecutive stages is seldom if ever achieved. During the analysis stage, it might turn out that the specifications are incomplete or inconsistent. A problem found during testing requires at least a brief return to the coding stage. If it is serious enough, it might even require a new design. Maintenance usually involves redoing some of the work from previous stages....)

Large, complex programming projects are only likely to succeed if a careful, systematic approach is adapted during all stages of the software life cycle. The systematic approach to programming, using accepted principles of good design, is called software engineering. The software engineer tries to efficiently construct programs that verifyably meet their specifications and that are easy to modify if necessary. There is a wide range of "methodologies" that can be applied to help in the systematic design of programs. (Most of these methodologies seem to involve drawing little boxes to represent program components, with labeled arrows to represent relationships among the boxes.)

We have been discussing object orientation in programming languages, which is relevant to the coding stage of program development. But there are also object-oriented methodologies for analysis and design. The question here is, how can one discover or invent the overall structure of a program? As an example of a rather simple object-oriented approach to analysis and design, consider this advice: Write down a description of the problem. Underline all the nouns in that description. The nouns should be considered as candidates for becoming classes or objects in the program design. Similarly, underline all the verbs. These are candidates for methods. This is your starting point. Further analysis might uncover the need for more classes and methods, and it might reveal how the classes can be organized into class hierarchies to take advantage of similarities among classes.

This is perhaps a bit simple-minded, but the idea is clear: Analyze the problem to discover the concepts that are involved, and create classes to represent those concepts. The design should arise from the problem itself, and you should end up with a program whose structure reflects the structure of the problem in a natural way.


Generalized Software Components

Every programmer builds up a stock of techniques and expertise expressed as snippets of code that can be reused in new programs using the tried-and-true method of cut-and-paste: The old code is physically copied into the new program and then edited to customize it as necessary. The problem is that the editing is error-prone and time-consuming, and the whole enterprise is dependent on the programmer's ability to pull out that particular piece of code from last year's project that looks like it might be made to fit. (On the level of a corporation that wants to save money by not reinventing the wheel for each new project, just keeping track of all the old wheels becomes a major task.)

Well-designed classes are software components that can be reused without editing. If a class needs to be customized, a subclass can be created, and additions or modifications can be made in the subclass without making any changes to the original class. This can be done even if the programmer doesn't have access to the source code of the class and doesn't know any details of its internal, hidden implementation.

Some classes are designed specifically as a basis for making subclasses. For example, the package java.awt includes a class called Frame. An object of this class represents a window on the screen in a graphical user interface. A Frame object has many of the usual behaviors associated with such windows: It can be dragged and resized, for example. All it lacks is content. A Frame window is completely blank. A programmer who wants a window can create a subclass of Frame and override a few methods in order to add some content to the window. Much of the behavior of that window is inherited from Frame and does not have to be reprogrammed.


Let's look at an example of building on previous work. Suppose I want to work with mosaics of colored squares, where the colors are symmetrically arranged, as shown in the illustration on the right. Each time a square is colored, the squares that can be obtained from that one by horizontal and vertical reflection through the center of the mosaic are also set to the same color. The four red squares in the picture, for example, form a set of such symmetrically placed squared, as do the purple squares and the green squares. (The blue square is at the center of the mosaic, so reflecting it doesn't produce any other squares; it's its own reflection.)

How can I implement such a grid of squares? I could do so from scratch, but in Section 3.6, I discussed a class, MosaicFrame, that implements a grid of colored squares (not necessarily symmetrical). If that class is available, perhaps I can build on it to produce a subclass, SymmetricMosaicFrame, that always displays a symmetrical pattern of colored squares. In fact, the idea is simple: A SymmetricMosaicFrame is exactly like a MosaicFrame except that whenever the color of a square is set, the color of the symmetrically placed squares should also be set. It does require a bit of mathematics to figure out how to specify those squares, and you can accept my formulas on faith if you want. But here is how it works: If the number of rows of squares is given by the variable rows, then the vertical reflection of the square in position (R,C) is in position (rows-1-R,C). This is because row R is R rows down from the top row, which is row number zero, so its vertical reflection is R rows up from the bottom row, which is number rows-1. The formulas for the other two reflected squares are found in a similar way.

Now, the class MosaicFrame already has a method, setColor(), for setting the color of a single square. We can override this method so that it also sets the colors of the symmetrically placed squares. To set the color of each square, we need to call the setColor() method from the superclass, MosaicFrame. To do this, we use the special variable super, which was described in Section 2. The setColor() method in the SymmetricMosaicFrame class can be defined like this:

        public void setColor(int R, int C, int r, int g, int b) {
                  // Set color of square in row R, column C to the color
                  // with red, blue, and green components given by 
                  // r, g, and b.  Also set the colors of the 
                  // squares obtained from this one by horizontal
                  // and vertical reflection.
                super.setColor(R, C, r, g, b);
                super.setColor(R, columns - 1 - C, r, g, b);
                super.setColor(rows - R - 1, C, r, g, b);
                super.setColor(rows - R - 1, columns - 1 - C, r, g, b);
            }

With this definition, squares will be colored in groups in order to preserve symmetry. This assumes that rows and columns are instance variables in the class SymmetricMosaicFrame. Perhaps these variables (or similar variables with different names) already exist in the MosaicFrame class. If so, they will be inherited by the SymmetricMosaicFrame class. Otherwise, I have to define rows and columns in my class and make sure that their values are set correctly. (And that's what I'll do in my example.)

Now, if we want to be able to create mosaic windows of different sizes, our class will need a constructor in which the number of rows and columns can be specified as parameters. The MosaicFrame class already has such a constructor:

MosaicFrame(int rows, int columns)

The constructor for the SymmetricMosaicFrame class can call this constructor to do all the actual work of opening the window and setting up the basic mosaic. (Note that I don't have to know how to do any of this myself. I only have to know that it can be done by calling the constructor.) The only other thing that needs to be done by the SymmetricMosaicFrame constructor is to set the values of the instance variables rows and columns to record the number of rows and columns in the mosaic. So, the constructor we want looks like this:

          public SymmetricMosaicFrame(int rows, int columns) {
             super(rows,columns);     // Call constructor from superclass.
             this.rows = rows;        // Remember the number of rows and
             this.columns = columns;  //    columns, for use in setColor().
          }

Putting all the pieces together -- but leaving out most of the comments -- the full definition of the SymmetricMosaicFrame class is as follows:

          public class SymmetricMosaicFrame extends MosaicFrame {

             int rows, columns; // number of rows and columns in mosaic
             
             public SymmetricMosaicFrame(int rows, int columns) {   // constructor
                super(rows,columns);
                this.rows = rows;
                this.columns = columns;
             }
   
             public void setColor(int R, int C, int r, int g, int b) {
                super.setColor(R, C, r, g, b);
                super.setColor(R, columns - 1- C, r, g, b);
                super.setColor(rows - R - 1, C, r, g, b);
                super.setColor(rows - R - 1, columns - 1 - C, r, g, b);
             }

          }  // end class SymmetricMosaicFrame

This gives a rather nice result for so little work!

In Section 3.6, I used the MosaicFrame class in a program that showed a "disturbance" wandering around in a random mosaic, changing colors as it goes. It's worth noting that we could modify that program to use a symmetric mosaic simply by replacing the line

MosaicFrame mosaic = new MosaicFrame(rows,columns);

in the original program with

MosaicFrame mosaic = new SymmetricMosaicFrame(rows,columns);

Then, whenever the program calls mosaic.setColor(), it is the setColor() method from the SymmetricMosaicFrame that is executed. So, the colors of a whole symmetric set of squares will be changed. (By the way, after looking at the result, I decided it looked better to have the disturbance wander on an initially black background, rather than on a mosaic of symmetrically colored squares.)


End of Chapter 4

[ Next Chapter | Previous Section | Chapter Index | Main Index ]