You are on page 1of 10

CS 112: Supplementary Notes Structure Charts and Bottom-up Implementation

Java Version

David Wolber

Structure charts are used to specify the high-level design, or architecture, of a computer program. As a design tool, they aid the programmer in dividing and conquering a large software problem, that is, recursively breaking a problem down into parts that are small enough to be understood by a human brain. The process is called top-down design, or functional decomposition. Programmers use a structure chart to build a program in a manner similar to how an architect uses a blueprint to build a house. In the design stage, the chart is drawn and used as a way for the client and the various software designers to communicate. During the actual building of the program (implementation), the chart is continually referred to as the master-plan. Often, it is modified as programmers learn new details about the program. After a program is completed, the structure chart is used to fix bugs and make changes (maintenance). It is especially important in this stage, because often the original architect is long gone, and a programmer new to the project uses the chart as a navigational tool into the often huge set of source code files. If you've ever experienced the joy of modifying someone else's program, you know how important this blue print can be. The first step in creating a structure is to place Class::main in the root of an upsidedown tree which forms the structure chart. The next step is to conceptualize the main sub-tasks that must be performed by the program to solve the problem. These subtasks are placed in nodes below the root, and connecting lines are drawn from the root to each sub-task. Next, the programmer focuses on each sub-task individually, and conceptualizes how each can be broken down into even smaller tasks. Eventually, the program is broken down to a point where the leaves of the tree represent simple methods that can be coded with just a few program statements. Once the structure chart has been designed, it is used to drive the software implementation. Branches of the tree are assigned as modules to be programmed by various members of the development team. Often, a process called bottom-up implementation and testing is used to implement each module. This process will be discussed in detail later in this document. Consider the following sample structure chart. Many information processing applications can be modeled with a chart similar to this sample:
Root is always main

Foo:main Each node represents a method. parameter of DisplayResults

inputData
Foo:GetInput

inputData

result

result
Foo:DisplayResults

Foo:ProcessInput

Figure 1. A Sample Structure Chart. Note that in this sample, all methods are from the class Foo. In general, the methods of a structure chart will come from various classes. And structure charts for real-world applications tend to be many levels deep and contain hundreds or thousands of methods. Each node in the chart represents a method (in object-oriented terms, a method). The root node represents the main method of the program. Each connecting line represents a method call. The connecting lines are labeled with the parameters of the method represented by the lower node. For example, the structure chart above could correspond to the program code displayed below:
public class Foo { public static void main (String[] args) { InputData inputData=null; int result=0; Foo foo = new Foo(); foo.GetInput(inputData); result=foo.processInput(inputData); foo.displayResults(result);} } //methods getInput, processInput, and displayResults not shown }

However, there is not a one-to-one correspondence between a structure chart and a program; a number of programs could be written that follow the design of any structure chart. A structure chart is a high-level design notation, and leaves some

coding details unspecified. Specifically, control structures for iteration (e.g., for, while) and selection (if-then-else, switch) are not depicted in a structure chart; The chart only shows the calling sequence of a program (left-to-right ordering is important), and its parameter information. All the above structure chart denotes is that the main program will contain a call to getInput, processInput, and displayResults, in that order - it says nothing about other program statements that might be interleaved with the method calls. For instance, the following is an alternative program that also corresponds to the sample structure chart:
public class Foo { public static void main (String[] args) { InputData inputData=null; int result=0; int i=0; Foo foo = new Foo(); foo.getInput(inputData); if (i>3) result = foo.processInput(inputData); else for (i=0;i<5;i++) foo.displayResults(result); } }

Parameter Passing The connecting lines in a structure chart are annotated with the names of the parameters that are sent in the method call. Each parameter is denoted as in, out, or in-out, with the following arrow types: in: out: in/out:

An in parameter data that the calling method sends to the callee. All pass-by-value parameters are in parameters. If the actual parameter sent is a variable (e.g. x) and not a constant (e.g., 3), that variable will not be effected by the method call.

In Java, all basic type parameters (e.g., int) are sent by-value, and thus are in parameters. There is no way to send a basic type parameter by-reference, as in C or C++. There are also no automatically allocated objects, only references, so all objecttype parameters are sent by-reference. But if an object is sent to a method as a parameter, and the method doesn't change the data within the object, we still consider the object parameter an in parameter. An out corresponds to either a pass-by-reference parameter or the return value of the method. The value of the out parameter sent in by the caller is not accessed by the called method. The called method sets the value of the parameter (or return value) somewhere within its code. One way to look at this kind of parameters passing is that the calling method sends a blank piece of paper to the called method, which puts something on the paper and sends it back. An in-out parameter is coded with a pass-by-reference parameter. The value of the parameter sent in is accessed by the called method, and the called method modifies the value (e.g., with an x = x+1 statement). One way to look at in-out parameter passing is that the caller sends the called method a piece of paper with a value on it, and the called method looks at the value on the paper, modifies it, and sends it back. By specifying each parameter as in,out,or in-out, the designer clearly states the effect of each method. If no global variables are allowed, the effect of a method is the change to the out and in-out parameters (note that methods can also effect program execution by performing input/output statements,e .g., cout, printf). Thus, just by analyzing the parameters and arrows on a structure chart, and without delving into code, a programmer can begin to understand a large program. The Object is a Parameter In Java and other object-oriented programs, each method call has an implicit parameter which is the object itself. For instance, with the following call:
Goo goo; int x; foo.meth(x, goo)

three parameters actually get sent to the method "meth"s activation record, foo, x, and goo. In our initial example structure chart, the implicit parameter was ignored, but in practice, it should appear in the structure chart. It should be represented as a parameter on the connecting lines of a structure chart, using the name "this", as in the following example:
this x goo

Foo:meth

Note that the "this" parameter can be specified as in, out, or in-out, just like any other parameter, depending on whether the method accesses and/or modifies the data members. The example shown specifies that meth will access foo's (this's) data members, but not modify them. Bottom-Up Implementation and Testing A bottom-up scheme begins with the coding and testing of a method found on the bottom of the structure chart tree (such a method node is referred to as a leaf of the tree). Leaf methods do not call any other methods, so they can be coded and tested independently. As each method is coded, it is tested to verify that the expected output is derived from various input sets. Test programs, called driver programs or unit tests, are used to test a method. Unit tests are mini-applications with main methods that 1) read in input values for the in and in-out parameters of the method being tested, then 2) call the method, then 3) display the values of the out and in-out parameters returned from the method. Generally, loops are used in the unit tests to iterate through various input sets so as to verify that the method works in general. Note that unit tests are not part of the final application that is developed they are used only by the programmer to test his code as he works. They can also be used during maintenance to re-test methods after they are modified.

After the leaves of the tree are coded and tested, the programmer traverses up the tree to code and test methods that call only leaf methods. As these mid-level methods are tested, the programmer is able to assume that the leaf methods work correctly, since they have already been tested. Eventually, the root node is coded and tested. By this time, the programmer should be confident that the left- and right-subtree code works correctly. When the root node is tested to work correctly, the program is complete. Figure 2 below shows the order by which the methods depicted in a structure chart are coded, if a bottom-up implementation scheme is used.
3 2 1 1 1

Figure 2. A structure chart, and the order each node is implemented/tested Unit tests for object-oriented programs can either reside in the class where the methods being tested reside, or in a separate class created just for testing purposes. In the following sample, a class TestFooGetInput was created to test Foo's GetInput method:

public class TestFooGetInput { public static void main (String[] args) {

Foo foo = new Foo(); InputData inputData=null; // no in parameters, so dont read anything // call the method for (;;) { foo.getInput(inputData);

// output the fields of inputData parameter System.out.println("inputData:"+inputData.toString()); } }

Here is a sample unit test for ProcessInput:


public class TestFooProcessInput {
public static void main (String[] args) { InputData inputData=null; int result=0; Foo foo = new Foo(); // need to loop through various input sets for (;;) { // need to read in parameter inputData foo.getInput(inputData); // now call method to be tested result=foo.processInput(inputData); // inputData is in-out,result is out, so display them System.out.println("inputData:+ inputData.toString()); System.out.println("result:"+result.toString()); } } }

Unit tests may seem like a bother in the short term, but in large-scale, real-world applications, knowing that method or class works correctly is like having a piece of gold. The idea is that through your unit tests, you'll find all the small bugs as you work, and in this way, you won't run into any big bugs that take hours, days, t.a.s and professors to fix. AVOID

THE BIG BUG!

Building an Application with Top-Down Design and Bottom-Up Implementation 1. Break down the problem into parts, sub-parts, etc. This decomposition actually has two parts: data decomposition and method decomposition. The result is a set of classes that will be used and a structure chart. During this stage you may also use some pseudo-code to sketch out important parts of the main method and other methods as well. 2. When you are satisfied with the overall design, begin implementing each method in a bottom-up order. After writing each method Class::Method, write the unit test for it in a class named "TestClassMethod". Compile and run the unit test, and be sure to try the method with various input sets until you are confident the method runs correctly for all possible inputs (parameters). 3. Eventually, you will have a set of unit test files, and a single file containing the code for the program. At this point, write the main method for the program. Note that the unit test classes are not part of this program, but be sure to keep them around for re-testing when methods are modified. After completing the program, revisit the structure chart to ensure that it is consistent with the code. This step is purely bookkeeping and often ignored, but it is important if you or someone else will modify the program in the future.

You might also like