## Section 4.6

More on Program Design

UNDERSTANDING HOW PROGRAMS WORK IS ONE THING. Designing a program to perform some particular task is another thing altogether. In Section 3.2, I discussed how stepwise refinement can be used to methodically develop an algorithm. We can now see how subroutines can fit into the process.

Stepwise refinement is inherently a top-down process, but the process does have a "bottom," that is, a point at which you stop refining the pseudocode algorithm and translate what you have directly into proper programming language. In the absence of subroutines, the process would not bottom out until you get down to the level of assignment statements and very primitive input/output operations. But if you have subroutines lying around to perform certain useful tasks, you can stop refining as soon as you've managed to express your algorithm in terms of those tasks.

This allows you to add a bottom-up element to the top-down approach of stepwise refinement. Given a problem, you might start by writing some subroutines that perform tasks relevant to the problem domain. The subroutines become a toolbox of ready-made tools that you can integrate into your algorithm as you develop it. (Alternatively, you might be able to buy or find a software toolbox written by someone else, containing subroutines that you can use in your project as black boxes.)

Subroutines can also be helpful even in a strict top-down approach. As you refine your algorithm, you are free at any point to take any sub-task in the algorithm and make it into a subroutine. Developing that subroutine then becomes a separate problem, which you can work on separately. Your main algorithm will merely call the subroutine. This, of course, is just a way of breaking your problem down into separate, smaller problems. It is still a top-down approach because the top-down analysis of the problem tells you what subroutines to write. In the bottom-up approach, you start by writing or obtaining subroutines that are relevant to the problem domain, and you build your solution to the problem on top of that foundation of subroutines.

## Preconditions and Postconditions

When working with subroutines as building blocks, it is important to be clear about how a subroutine interacts with the rest of the program. This interaction is specified by the contract of the subroutine, as discussed in Section 1. A convenient way to express the contract of a subroutine is in terms of preconditions and postconditions.

The precondition of a subroutine is something that must be true when the subroutine is called, if the subroutine is to work correctly. For example, for the built-in function

Math.sqrt(x), a precondition is that the parameter,x, is greater than or equal to zero, since it is not possible to take the square root of a negative number. In terms of a contract, a precondition represents an obligation of thecallerof the subroutine. If you call a subroutine without meeting its precondition, then there is no reason to expect it to work properly. The program might crash or give incorrect results, but you can only blame yourself, not the subroutine.A postcondition of a subroutine represents the other side of the contract. It is something that will be true after the subroutine has run (assuming that its preconditions were met -- and that there are no bugs in the subroutine). The postcondition of the function

Math.sqrt()is that the square of the value that is returned by this function is equal to the parameter that is provided when the subroutine is called. Of course, this will only be true if the preconditiion -- that the parameter is greater than or equal to zero -- is met. A postcondition of the built-in subroutineSystem.out.print()is that the value of the parameter has been displayed on the screen.Preconditions most often give restrictions on the acceptable values of parameters, as in the example of

Math.sqrt(x). However, they can also refer to global variables that are used in the subroutine. The postcondition of a subroutine specifies the task that it performs. For a function, the postcondition should specify the value that the function returns.Subroutines are often described by comments that explicitly specify their preconditions and postconditions. When you are given a pre-written subroutine, a statement of its preconditions and postcondtions tells you how to use it and what it does. When you are assigned to write a subroutine, the preconditions and postconditions give you an exact specification of what the subroutine is expected to do. I will use this approach in the example that constitutes the rest of this section. I will also use it occasionally later in the book, although I will generally be less formal in my commenting style.

Preconditions and postconditions will be discussed more thoroughly in Chapter 9, which deals with techniques for writing correct and robust programs.

## A Design Example

Let's work through an example of program design using subroutines. In this example, we will both use prewritten subroutines as building blocks and design new subroutines that we need to complete the project.

Suppose that I have found an already-written class called

Mosaic. This class allows a program to work with a window that displays little colored rectangles arranged in rows and columns. The window can be opened, closed, and otherwise manipulated with static member subroutines defined in theMosaicclass. Here are some of the available routines:void Mosaic.open(int rows, int cols, int w, int h); Precondition: The parameters rows, cols, w, and h are greater than zero. Postcondition: A "mosaic" window is opened on the screen that can display rows and columns of colored rectangles. Each rectangle is w pixels wide and h pixels high. The number of rows is given by the first parameter and the number of columns by the second. Initially, all the rectangles are black. Note: The rows are numbered from 0 to rows - 1, and the columns are numbered from 0 to cols - 1. void Mosaic.setColor(int row, int col, int r, int g, int b); Precondition: row and col are in the valid ranges of row numbers and column numbers. r, g, and b are in the range 0 to 255. Also, the mosaic window should be open. Postcondition: The color of the rectangle in row number row and column number col has been set to the color specified by r, g, and b. r gives the amount of red in the color with 0 representing no red and 255 representing the maximum possible amount of red. The larger the value of r, the more red in the color. g and b work similarly for the green and blue color components. int Mosaic.getRed(int row, int col); int Mosaic.getBlue(int row, int col); int Mosaic.getGreen(int row, int col); Precondition: row and col are in the valid ranges of row numbers and column numbers. Also, the mosaic window should be open. Postcondition: Returns an int value that represents one of the three color components of the rectangle in row number row and column number col. The return value is in the range 0 to 255. (Mosaic.getRed() returns the red component of the rectangle, Mosaic.getGreen() the green component, and Mosaic.getBlue() the blue component.) void Mosaic.delay(int milliseconds); Precondition: milliseconds is a positive integer. Postcondition: The program has paused for at least the number of milliseconds given by the parameter, where one second is equal to 1000 milliseconds. Note: This can be used to insert a time delay in the program (to regulate the speed at which the colors are changed, for example). boolean Mosaic.isOpen(); Precondition: None. Postcondition: The return value is true if the mosaic window is open on the screen, and is false otherwise. Note: The window will be closed if the user clicks its close box. It can also be closed programmatically by calling the subroutine Mosaic.close().My idea is to use the

Mosaicclass as the basis for a neat animation. I want to fill the window with randomly colored squares, and then randomly change the colors in a loop that continues as long as the window is open. "Randomly change the colors" could mean a lot of different things, but after thinking for a while, I decide it would be interesting to have a "disturbance" that wanders randomly around the window, changing the color of each square that it encounters. Here's an applet that shows what the program will do:

With basic routines for manipulating the window as a foundation, I can turn to the specific problem at hand. A basic outline for my program is

Open a Mosaic window Fill window with random colors; Move around, changing squares at random.Filling the window with random colors seems like a nice coherent task that I can work on separately, so let's decide to write a separate subroutine to do it. The third step can be expanded a bit more, into the steps: Start in the middle of the window, then keep moving to a new square and changing the color of that square. This should continue as long as the mosaic window is still open. Thus we can refine the algorithm to:

Open a Mosaic window Fill window with random colors; Set the current position to the middle square in the window; As long as the mosaic window is open: Randomly change color of current square; Move current position up, down, left, or right, at random;I need to represent the current position in some way. That can be done with two

intvariables namedcurrentRowandcurrentColumn. I'll use 10 rows and 20 columns of squares in my mosaic, so setting the current position to be in the center means settingcurrentRowto 5 andcurrentColumnto 10. I already have a subroutine,Mosaic.open(), to open the window, and I have a function,Mosaic.isOpen(), to test whether the window is open. To keep the main routine simple, I decide that I will write two more subroutines of my own to carry out the two tasks in the while loop. The algorithm can then be written in Java as:Mosaic.open(10,20,10,10) fillWithRandomColors(); currentRow = 5; // Middle row, halfway down the window. currentColumn = 10; // Middle column. while ( Mosaic.isOpen() ) { changeToRandomColor(currentRow, currentColumn); randomMove(); }With the proper wrapper, this is essentially the

main()routine of my program. It turns out I have to make one small modification: To prevent the animation from running too fast, the line "Mosaic.delay(20);" is added to thewhileloop.The

main()routine is taken care of, but to complete the program, I still have to write the subroutinesfillWithRandomColors(),changeToRandomColor(int,int), andrandomMove(). Writing each of these subroutines is a separate, small task. ThefillWithRandomColors()routine is defined by the postcondition that "each of the rectangles in the mosaic has been changed to a random color." Pseudocode for an algorithm to accomplish this task can be given as:For each row: For each column: set the square in that row and column to a random color"For each row" and "for each column" can be implemented as for loops. We've already planned to write a subroutine

changeToRandomColorthat can be used to set the color. (The possibility of reusing subroutines in several places is one of the big payoffs of using them!) So,fillWithRandomColors()can be written in proper Java as:static void fillWithRandomColors() { for (int row = 0; row < 10; row++) for (int column = 0; column < 20; column++) changeToRandomColor(row,column); }Turning to the

changeToRandomColorsubroutine, we already have a method,Mosaic.setColor(row,col,r,g,b), that can be used to change the color of a square. If we want a random color, we just have to choose random values forr,g, andb. According to the precondition of theMosaic.setColor()subroutine, these random values must be integers in the range from 0 to 255. A formula for randomly selecting such an integer is "(int)(256*Math.random())". So the random color subroutine becomes:static void changeToRandomColor(int rowNum, int colNum) { int red = (int)(256*Math.random()); int green = (int)(256*Math.random()); int blue = (int)(256*Math.random()); mosaic.setColor(rowNum,colNum,red,green,blue); }Finally, consider the

randomMovesubroutine, which is supposed to randomly move the disturbance up, down, left, or right. To make a random choice among four directions, we can choose a random integer in the range 0 to 3. If the integer is 0, move in one direction; if it is 1, move in another direction; and so on. The position of the disturbance is given by the variablescurrentRowandcurrentColumn. To "move up" means to subtract 1 fromcurrentRow. This leaves open the question of what to do ifcurrentRowbecomes -1, which would put the disturbance above the window. Rather than let this happen, I decide to move the disturbance to the opposite edge of the applet by settingcurrentRowto 9. (Remember that the 10 rows are numbered from 0 to 9.) Moving the disturbance down, left, or right is handled similarly. If we use aswitchstatement to decide which direction to move, the code forrandomMovebecomes:int directionNum; directoinNum = (int)(4*Math.random()); switch (directionNum) { case 0: // move up currentRow--; if (currentRow < 0) // CurrentRow is outside the mosaic; currentRow = 9; // move it to the opposite edge. break; case 1: // move right currentColumn++; if (currentColumn >= 20) currentColumn = 0; break; case 2: // move down currentRow++; if (currentRow >= 10) currentRow = 0; break; case 3: // move left currentColumn--; if (currentColumn < 0) currentColumn = 19; break; }Putting this all together, we get the following complete program. The variables

currentRowandcurrentColumnare defined as static members of the class, rather than local variables, because each of them is used in several different subroutines. This program actually depends on two other classes,Mosaicand another class calledMosaicCanvasthat is used byMosaic. If you want to compile and run this program, both of these classes must be available to the program.public class RandomMosaicWalk { /* This program shows a window full of randomly colored squares. A "disturbance" moves randomly around in the window, randomly changing the color of each square that it visits. The program runs until the user closes the window. */ static int currentRow; // row currently containing the disturbance static int currentColumn; // column currently containing disturbance public static void main(String[] args) { // Main program creates the window, fills it with // random colors, then moves the disturbance in // a random walk around the window for as long as // the window is open. Mosaic.open(10,20,10,10); fillWithRandomColors(); currentRow = 5; // start at center of window currentColumn = 10; while (Mosaic.isOpen()) { changeToRandomColor(currentRow, currentColumn); randomMove(); Mosaic.delay(20); } } // end of main() static void fillWithRandomColors() { // Precondition: The mosaic window is open. // Postcondition: Each rectangle has been set to a // random color for (int row=0; row < 10; row++) { for (int column=0; column < 20; column++) { changeToRandomColor(row, column); } } } // end of fillWithRandomColors() static void changeToRandomColor(int rowNum, int colNum) { // Precondition: rowNum and colNum are in the valid range // of row and column numbers. // Postcondition: The rectangle in the specified row and // column has been changed to a random color. int red = (int)(256*Math.random()); // choose random levels in range int green = (int)(256*Math.random()); // 0 to 255 for red, green, int blue = (int)(256*Math.random()); // and blue color components Mosaic.setColor(rowNum,colNum,red,green,blue); } // end of changeToRandomColor() static void randomMove() { // Precondition: The global variables currentRow and currentColumn // specify a valid position in the grid. // Postcondition: currentRow or currentColumn is changed to // one of the neighboring positions in the grid, // up, down, left, or right from the previous // position. If this moves the position outside // the grid, then it is moved to the opposite edge // of the window. int directionNum; // Randomly set to 0, 1, 2, or 3 to choose direction. directionNum = (int)(4*Math.random()); switch (directionNum) { case 0: // move up currentRow--; if (currentRow < 0) currentRow = 9; break; case 1: // move right currentColumn++; if (currentColumn >= 20) currentColumn = 0; break; case 2: // move down currentRow ++; if (currentRow >= 10) currentRow = 0; break; case 3: // move left currentColumn--; if (currentColumn < 0) currentColumn = 19; break; } } // end of randomMove() } // end of class RandomMosaicWalk

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