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 acheived. During the analysis stage, it might turn out that the specifications are incomplete or inconsistant. 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.)
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 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, MosaicWindow, that implements a grid of colored squares (not necessarily symmetrical). If that class is available, perhaps I can build on it to produce a SymmetricMosaic class that would always display a symmetrical pattern of colored squares. In fact, the idea is simple: A SymmetricMosaic is exactly like a MosaicWindow 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, using the fact that the rows are numbered from 0 to ROWS-1, and the columns from 0 to COLUMNS-1. (You can verify the formulas I will use, if you want.)
Now, the class MosaicWindow 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, MosaicWindow. To do this, we use the special variable super, as described in the previous section. So, the class SymmetricMosaic can be defined like this:
class SymmetricMosaic extends MosaicWindow { void setColor(int R, int C, double r, double g, double 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); } }That should do it! In a SymmetricMosaic, squares will always be colored in groups in order to preserve symmetry. This assumes, by the way, that ROWS and COLUMNS are instance variables in the class MosaicWindow that give the number of rows and columns in the mosaic. It also assumes that ROWS and COLUMNS are not declared to be private in the class MosaicWindow; if they were, they could not legally be used here. Most likely, they would be declared to be protected, to make them accessible in subclasses of MosaicWindow.
That should do it, but it's worth noting that when I actually tried this, I found that I couldn't make any objects of type SymmetricMosaic! The problem was that MosaicWindow had just one constructor,
MosaicWindow(int ROWS, int COLUMNS)
This constructor specifies the number of rows and columns that are desired in the mosaic. The problem is that every constructor in the subclass SymmetricMosaic must call the constructor of its superclass. So, I have to provide a constructor in the subclass simply to call the superclass's constructor. See the previous section for a discussion of constructors in subclasses. (This is a technicality that I think could have been avoided if Java had been designed a little better in this regard.) Here is a complete, correct version of class SymmetricMosaic:
class SymmetricMosaic extends MosaicWindow { SymmetricMosaic(int ROWS, int COLUMNS){ // constructor super(ROWS, COLUMNS); // call constructor from superclass } void setColor(int R, int C, double r, double g, double 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); } }In Section 3.6, I used the MosaicWindow 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
MosaicWindow mosaic = new MosaicWindow(ROWS,COLUMNS);
with
MosaicWindow mosaic = new SymmetricMosaic(ROWS,COLUMNS);
All the calls to mosaic.setColor() in that program will then set the color of symmetrical groups of squares. (By the way, after looking at the result, I decided that the eye cannot readily detect symmetry in a mosaic based on color alone, and I decided that it looked better to have the disturbance wander on an initially white background, rather than on a mosaic of symmetrically colored squares.)
End of Chapter 4
[ Next Chapter | Previous Section | Chapter Index | Main Index ]