Suppose that you are an engineer working in industry and that you need to write a program to solve a particular problem. How do you begin?
When given a new problem, there is a natural tendency to sit down at a key- board and start programming without “wasting” a lot of time thinking about the problem first. It is often possible to get away with this “on-the-fly” approach to programming for very small problems, such as many of the examples in this book. In the real world, however, problems are larger, and a programmer attempt- ing this approach will become hopelessly bogged down. For larger problems, it pays to completely think out the problem and the approach you are going to take to it before writing a single line of code.
We will introduce a formal program design process in this section and then apply that process to every major application developed in the remainder of the book. For some of the simple examples that we will be doing, the design process will seem like overkill. However, as the problems that we solve get larger and larger, the process becomes more and more essential to successful programming.
When I was an undergraduate, one of my professors was fond of saying,
“Programming is easy. It’s knowing what to program that’s hard.” His point was forcefully driven home to me after I left university and began working in industry on larger-scale software projects. I found that the most difficult part of my job was to understand the problemI was trying to solve. Once I really understood the problem, it became easy to break the problem apart into smaller, more easily manageable pieces with well-defined functions, and then to tackle those pieces one at a time.
Top-down designis the process of starting with a large task and breaking it down into smaller, more easily understandable pieces (sub-tasks) which perform a portion of the desired task. Each sub-task may in turn be subdivided into smaller sub-tasks if necessary. Once the program is divided into small pieces, each piece can be coded and tested independently. We do not attempt to combine the sub- tasks into a complete task until each of the sub-tasks has been verified to work properly by itself.
The concept of top-down design is the basis of our formal program design process. We now introduce the details of the process, which is illustrated in Fig- ure 3.1. The steps involved are as follows.
1. Clearly state the problem that you are trying to solve.
Programs are usually written to fill some perceived need, but that need may not be articulated clearly by the person requesting the program.
For example, a user may ask for a program to solve a system of simul- taneous linear equations. This request is not clear enough to allow a programmer to design a program to meet the need; he or she must first know much more about the problem to be solved. Is the system of equations to be solved real or complex? What is the maximum number of equations and unknowns that the program must handle? Are there any symmetries in the equations which might be exploited to make the task easier? The program designer will have to talk with the user requesting the program, and the two of them will have to come up with a clear statement of exactly what they are trying to accomplish. A clear statement of the problem will prevent misunderstandings, and it will
3.1 Introduction to Top-Down Design Techniques | 89
also help the program designer to properly organize his or her thoughts.
In the example we were describing, a proper statement of the problem might have been:
Design and write a program to solve a system of simultaneous linear equations having real coefficients and with up to 20 equations in 20 unknowns.
Start
State the problem you are trying to solve
Define required inputs and outputs
Decomposition
Stepwise refinement
Top-down design process Convert algorithms into
MATLAB statements Design the algorithm
Test the resulting MATLAB program
Finished!
Figure 3.1 The program design process used in this book.
2. Define the inputs required by the program and the outputs to be pro- duced by the program.
The inputs to the program and the outputs to be produced by the program must be specified so that the new program will properly fit into the overall processing scheme. In the preceding example, the coefficients of the equations to be solved are probably in some preexisting order, and our new program needs to be able to read them in that order. Similarly, it needs to produce the answers required by the programs that may follow it in the overall processing scheme, and to write out those answers in the format needed by the programs following it.
3. Design the algorithm that you intend to implement in the program.
An algorithm is a step-by-step procedure for finding the solution to a problem. It is at this stage in the process that top-down design techniques come into play. The designer looks for logical divisions within the prob- lem and divides it up into sub-tasks along those lines. This process is calleddecomposition. If the sub-tasks are themselves large, the designer can break them up into even smaller sub-sub-tasks. This process contin- ues until the problem has been divided into many small pieces, each of which does a simple, clearly understandable job.
After the problem has been decomposed into small pieces, each piece is further refined through a process called stepwise refinement. In step- wise refinement, a designer starts with a general description of what the piece of code should do and then defines the functions of the piece in greater and greater detail until they are specific enough to be turned into MATLAB statements. Stepwise refinement is usually done with pseudocode, which is described in the next section.
It is often helpful to solve a simple example of the problem by hand during the algorithm development process. If the designer understands the steps that he or she went through in solving the problem by hand, then he or she will be in better able to apply decomposition and stepwise refine- ment to the problem.
4. Turn the algorithm into MATLAB statements.
If the decomposition and refinement process was carried out properly, this step will be very simple. All the programmer will have to do is to replace pseudocode with the corresponding MATLAB statements on a one-for- one basis.
5. Test the resulting MATLAB program.
This step is the real killer. The components of the program must first be test- ed individually, if possible, and then the program as a whole must be tested.
When testing a program, we must verify that it works correctly for all legal input data sets. It is very common for a program to be written, tested with some standard data set, and released for use, only to find that it produces the wrong answers (or crashes) with a different input data set. If the algorithm implemented in a program includes different branches, we must test all of the
possible branches to confirm that the program operates correctly under every possible circumstance.
Large programs typically go through a series of tests before they are released for general use (see Figure 3.2). The first stage of testing is sometimes called unit testing. During unit testing, the individual sub-tasks of the program are tested sep- arately to confirm that they work correctly. After the unit testing is completed, the program goes through a series of buildsduring which the individual sub-tasks are 3.1 Introduction to Top-Down Design Techniques | 91
Figure 3.2 A typical testing process for a large program.
combined to produce the final program. The first build of the program typically includes only a few of the sub-tasks. It is used to check the interactions among those sub-tasks and the functions performed by the combinations of the sub-tasks. In suc- cessive builds, more and more sub-tasks are added, until the entire program is com- plete. Testing is performed on each build, and any errors (bugs) that are detected are corrected before moving on to the next build.
Testing continues even after the program is complete. The first complete ver- sion of the program is usually called the alpha release. It is exercised by the pro- grammers and others very close to them in as many different ways as possible, and the bugs discovered during the testing are corrected. When the most serious bugs have been removed from the program, a new version called the beta releaseis pre- pared. The beta release is normally given to “friendly” outside users who have a need for the program in their normal day-to-day jobs. These users put the program through its paces under many different conditions and with many different input data sets, and they report any bugs that they find to the programmers. When those bugs have been corrected, the program is ready to be released for general use.
Because the programs in this book are fairly small, we will not go through the sort of extensive testing described in the preceding text. However, we will fol- low the basic principles in testing all of our programs.
The program design process may be summarized as follows:
1. Clearly state the problem that you are trying to solve.
2. Define the inputs required by the program and the outputs to be produced by the program.
3. Design the algorithm that you intend to implement in the program.
4. Turn the algorithm into MATLAB statements.
5. Test the MATLAB program.
✷ Good Programming Practice:
Follow the steps of the program design process to produce reliable, understand- able MATLAB programs.
In a large programming project, the time actually spent programming is surprisingly small. In his book The Mythical Man-Month1, Frederick P. Brooks, Jr. suggests that in a typical large software project, one-third of the time is spent planning what to do (steps 1 through 3), one-sixth of the time is spent actually writing the program (step 4), and fully one-half of the time is spent in testing and debugging the program! Clearly, anything that we can do to reduce the testing and debugging time will be very helpful. We can best reduce the testing and debugging time by doing a very careful job in the planning phase and by using
1Frederick P. Brooks Jr.,The Mythical Man-Month, Anniversary Edition,Addison-Wesley, 1995.
good programming practices. Good programming practices will reduce the number of bugs in the program and will make the ones that do creep in easier to find.