Lab 2: Software Engineering


 

Introduction

Our problem today is to learn how to design and write a program. Since doing so is creating software, this endeavor is sometimes called software engineering. The particular design technique we will be using in this manual is called object-centered design (OCD), a methodology explicitly designed to help novice programmers get started writing software.

The process of developing a program to solve a problem involves several stages:

  1. Design: Carefully plan your program using OCD...
    1. Behavior: Describe as precisely as possible what the program must do;
    2. Objects: Using the behavioral description, identify the objects needed to solve the problem;
    3. Operations: Using the behavioral description, identify the operations needed to solve the problem;
    4. Algorithm: Organize the objects and operations into an algorithm -- a sequence of steps that solves the problem;
  2. Coding: Translating your algorithm into a programming language like Java;
  3. Testing: Running your program with sample data to check for errors, and debugging those errors until it can be deemed correct; and
  4. Maintenance: Performing any modifications needed to improve the program.

To illustrate these stages, today's exercise will use them to develop a program that, given the lengths of the two legs of a right triangle, will display the length of its hypotenuse.

 

Design

Yogi Berra once supposedly said,

   If you don't know where you're going,
   you'll end up somewhere else!

This idea is especially important when writing software: you need to "know where you are going" before you sit down to write a Java program. In order to "know where you are going", you need to spend some time designing your program.

A good design makes a program easy to write.

If you are not a novice programmer, spending the time to design a program may seem like a waste, especially at the beginning when problems are relatively easy. However, the problems will soon become more difficult. If you get in the habit of carefully designing your software now (while doing so is easy), then designing elegant solutions for more difficult problems will also be relatively easy. But if you take a "short cut" now and skip the design stage, you will not master object-centered design, and when the problems get more difficult, you will find that your peers are writing better programs and writing them faster than you.

So be disciplined and get into the habit of carefully designing your software. The remainder of this section will teach you how.

As we saw earlier, object-centered design consists of four sub-stages: describing how the program must behave to solve the problem, identifying its objects, identifying its operations, and organizing those objects and operations into an algorithm.

 

Behavior

We might describe how our program needs to behave as follows:

 Our program should first display on the screen a greeting, after which 
 it should display a prompt for the lengths of the legs of a right triangle.
 It should then read these two values from the keyboard.  Once it has the 
 two leg lengths, it should compute the hypotenuse length.  It should then 
 display the hypotenuse length (and appropriate labels) on the screen.

This behavioral description gives us all of the information we need to get started designing our program. In particular, it provides us with the objects and the operations our program requires to solve the problem. We will examine how next.

 

Objects

If you study our behavioral description and circle all of the nouns (ignoring nouns like "program" and "user"), the result is a list of the objects our program needs to define:

Description

Information
Type

Information Kind
(Varying or Constant)

Name

the screen

Screen

varying

theScreen

a greeting

String

constant

--

a prompt for the legs

String

constant

--

the first leg value

double

varying

leg1

the second leg value

double

varying

leg2

the keyboard

Keyboard

varying

theKeyboard

the hypotenuse value

double

varying

hypotenuse

labels for the hypotenuse

String

constant

--

For each noun, we have described some of its attributes:

These attributes are not strictly necessary at this point, but identifying them now saves us from having to do so later on.

Once we know the objects in our problem, it is useful to specify the basic flow of information in our program in terms of them. For example, we can write:

   Specification:
      input(Keyboard): leg1 and leg2, two double values.
      output(Screen): hypotenuse, a double value.

Such a specification succinctly states what the program does to solve the problem in terms of its inputs and outputs. (Output items like a greeting, prompts and labels are assumed.) It is good programming style to provide such a specification as part of the program's opening comment, for documentation purposes.

 

Operations

If you study our behavioral description and underline all of the verbs the result is a list of the operations our program needs to perform:

Description

Predefined?

Name

Package/Module?

display a String (the greeting)

yes

print()

ann.easyio

display a String (the prompt)

yes

print()

ann.easyio

read a double (the first leg value)

yes

readDouble()

ann.easyio

read a double (the second leg value)

yes

readDouble()

ann.easyio

compute the hypotenuse length

??

??

??

display a double (the hypotenuse)

yes

println()

ann.easyio

display a String (the label)

yes

print()

ann.easyio

In addition to listing the basic operations, we also list whether or not the operation is predefined; if so, how it is specified, and where the operation is defined. As with our objects, these extra steps are not necessary, but doing them now will save us time later on.

This table provides us with the basic operations needed to solve our problem; however, there is no predefined operation to compute the length of the hypotenuse of a right triangle. To do so, we must refine this operation by breaking it down into smaller ones. In particular, the Pythagorean Theorem tells us that we need these operations to compute the hypotenuse length:

Description

Predefined?

Name

Package/Module?

find the square of a double value

yes

pow()

Math

add two double values

yes

+

built-in

find the square root of a double value

yes

sqrt()

Math

store a double value in a variable

yes

=

built-in

If this seems like a lot of information to digest, don't get bogged down in the details. You will find that when you use a method and/or package frequently, you will naturally begin to remember its details, so all you need is practice. The important thing for now is to understand what steps we are taking to design our software, and why.

 

Algorithm

Once we have identified all of the objects and operations needed to solve our problem, we can organize them into a sequence of steps that solves our problem, which is called an algorithm for the problem. An algorithm should specify what a program does in fair detail, but it need not worry about the syntax details of a particular language like Java.

For our problem, we can organize the objects and operations as follows:

  1. Display via theScreen a greeting for the user.
  2. Display via theScreen prompts for the two leg lengths.
  3. Read from theKeyboard two double values, storing them in leg1 and leg2.
  4. Compute the hypotenuse length using the Pythagorean Theorem:
          hypotenuse = sqrt(pow(leg1, 2.0) + pow(leg2, 2.0))
    	
  5. Display via theScreen the value of hypotenuse along with an appropriate label.

This algorithm provides the blueprint for our Java program. Once we have it, we are done designing our program, and are ready to begin implementing our design.

 

Coding

As appropriate for your programming environment, begin by creating a new directory or project for this exercise and then save a copy of Hypot.java in the project directory. (Your instructor may have a local version for you to copy.) Hypot.java contains just a fragmentary opening comment (documentation that explains what the program does, who wrote it, and so on). Depending on your environment, you may need to add this file to your project.

Customize the opening comment with your name, the date, the name of your school, your course number, the problem specification, and any other information your instructor wishes it to contain.

Coding is the task of taking an algorithm and translating it into a high-level language like Java. We can begin this task by adding a minimal Java program to Hypot.java after the opening comment:



public class Hypot extends Object 
{
   public static void main(String args[])
   {
   
   }
}


Every Java file must contain a class with the same name (Hypot in this case.) that will contain the code to be executed. Every Java program which will be executed directly (not as an applet) must have a main function where execution begins and ends. The lines above make up a minimal main function, and thus provide a framework to which we can add other statements that perform the steps of our algorithm.

When you have added these lines to Hypot.java, translate your program into machine language, as we did in Lab 1. No errors or warnings should appear. (When you run this program, nothing should happen because it doesn't do anything yet.)

If error messages appear, then what you have entered contains syntax errors (i.e. your program is not a "grammatically correct Java sentence"). Errors in a program are frequently called bugs. In order for your program to compile, you must return to the editor and correct these errors, which is called debugging your program.

Once we have the minimal Java program constructed and can translate it, we are ready to encode our algorithm. To do so, we want to take each step of the algorithm and write one or more Java statements that perform that step. Step-wise translation is the process of translating an algorithm into a program, one step at a time. We therefore begin with the first step.

1. Display via theScreen a greeting for the user.

This step can be performed using an output statement. In general, to display a sequence of values value1... valueN on the screen, we can use the following output statement pattern:

   theScreen.print(value1);
   theScreen.print(value2);
   ...
   theScreen.print(valueN);

Add an output statement to your source program following the second { that displays the following message, using \n to print blank lines before and after the message, so that it is easy to read:

   Given two right triangle leg lengths,
    this program computes the hypotenuse length.

When you have added the commands to your source program, recompile it. The compiler should generate an error message like the following:

   Error   : undefined variable or class name: theScreen
   Hypot.java line 15   theScreen.print("\nGiven two right triangle leg lengths,");

Java requires that an object be declared before it can be used, using a declaration statement whose pattern is:

   Type Var1 = initialValue1,
        Var2 = initialValue2,
        ...
        VarN = initialValueN;

where Type is the types of the objects being declared, Var1, ..., VarN are the names of those objects, as specified back in our Design stage, and initialValue1, ..., initialValueN are the starting values for those objects (the = and the initial value are optional). In this case, we never declared what theScreen was supposed to be. Before we use it we need to add the following line just before the line containing main(String args[]){:

static Screen theScreen = new Screen();

When you have added this line to your source program, recompile it. The compiler should generate a new error message like the following:

   Error   : Class Screen not found.
   Hypot.java line 14   static Screen theScreen = new Screen();

The meaning of the method print is declared in a package named ann.easyio.Screen, not in your program. To access this declarations from that file, we can instruct the compiler to use the contents of ann.easyio.Screen in Hypot.java, by adding an import statement just before the line containing main(String args[]){:

import ann.easyio.*;

This import directive must appear in any program that performs output using the Screen class. Try to remember the error message (or make a note of it) so that if you see that message again, you will know how to "debug" that error.

Now, retranslate your program to check that what you have written is free of syntax errors. If not, you can infer that the error lies in the text you have added since your last correct compilation. Find your error(s) within those lines and use the editor to correct them.

When your source program compiles correctly, run it to test that it displays the intended message. If it does not, the statements you have added contain logic errors (i.e., your program is a valid Java 'sentence', but it 'says' something different than you intended.) Compare your source program statements against the output produced, and modify them as needed to correct the error.

When your program is error-free, proceed to the next step of our algorithm.

 

2. Display via theScreen prompts for the two leg lengths.

Like our first step, this step can also be accomplished using an output statement. Add to Hypot.java an output statement that displays the following prompt for input:

   Enter the two leg lengths: 

Use \n to leave a blank line between the greeting and this prompt, and leave a space after the colon. Check the correctness of what you have written by translating your program, and continue to the next step when it is correct.

 

3. Read from theKeyboard two double values, storing them in leg1 and leg2.

This step can be performed using an input statement. In general, to read a sequence of doubles from the keyboard and store them in Var1...VarN, we can use the following input statement pattern:

var1 = theKeyboard.readDouble();
   var2 = theKeyboard.readDouble();
...
   varN = theKeyboard.readDouble();

Using this pattern, add input statements to Hypot.java to perform step 3 of our algorithm. Then check its correctness by retranslating your program. Familiar errors should be generated.

In Java, the type double is used to define objects capable of storing real numeric values. One of the nice features of Java is that declarations are statements, and thus can appear anywhere a Java statement is permitted inside your main function. Insert a declaration for leg1 and leg2 without initialization just before your input statement. Then retranslate your program, and continue when it is free of syntax errors

 

4. Compute the hypotenuse length using the Pythagorean Theorem.

As we have seen, this step can be performed using the sqrt() and pow() methods that perform square root and exponentiation, respectively.

   hypotenuse = Math.sqrt(Math.pow(leg1, 2.0) + Math.pow(leg2, 2.0));

As noted previously, the sqrt() and pow() methods are declared in the class Math which is a part of the java.lang package. Anything in java.lang is automatically available to our program so we do not need to import java.lang.* .We do, however, need to prefix the methods with the class module that they belong to (Math in this case).

To perform this step, we will declare and initialize the object. Since hypotenuse is to store a double value, we write:

   double hypotenuse = Math.sqrt(Math.pow(leg1, 2.0) + Math.pow(leg2, 2.0));

Such a statement declares hypotenuse as a double object, and initializes it with the value of the expression to the right of the = symbol. Add this statement after your input statement (since it depends upon the values of leg1 and leg2). Then retranslate your program and continue to the final step when it is free of syntax errors.

5. Display via theScreen the value of hypotenuse, along with an appropriate label.

This final step of our algorithm is another two output statements. Suppose the value of hypotenuse is 1.414214, then we would like to see the message

   --> The hypotenuse length is: 1.414214

We can use a print() to get the first part and then a println() to display the value of hypotenuse followed by a return. Make sure that there are blank lines above the statement to make it stand out from the rest of your program. Retranslate your program, and when it is free of syntax errors, continue to the next stage of program development.

 

Testing

The third stage of program development is a thorough testing. The basic idea is to execute the program using sample data values that "exercise" the program, to see if it contains any logic errors. Execute Hypot with the following easy-to-check values, to see if you get the correct results.

leg1

leg2

predicted
hypotenuse

observed
hypotenuse

1.0

1.0

1.414214

________________

3.0

4.0

5.0

________________

5.0

12.0

13.0

________________

If you do not get the predicted results, then the error most likely lies in the formula by which you are calculating the hypotenuse value, so double-check it for accuracy and fix any mistakes it contains. Then retranslate and retest Hypot until you have a reasonable degree of confidence that it is correct.

 

Maintenance

Unlike programs that are written by students, "real world" programs may be used for many years. It is often the case that such programs must be modified several times over the course of their lifetimes, a task which is called program maintenance. Maintenance is thus the final (and usually the longest) stage of program development. Some studies have shown that the cost of maintaining a program can account for as much as 80% of its total cost! One of the goals of object-oriented programming is to try to reduce this maintenance cost, by writing code that is reusable.

To simulate program maintenance, suppose that Hypot should display only three decimal digits instead of the default number. To do so, its source program must be altered, so return to your text editor.

The number of decimal digits displayed for a real number is called the precision of that number. To show only three decimal digits, we will use a specialized printing method which is supplied by the ann.easyio package. The method printFormatted() has the signature

   printFormatted( DoubleExpression , IntegerExpression )

and will print the value of the double expression using the value of the integer expression as the precision. Before we printed the value of the hypotenuse using a println(). Since printFormatted does not allow us to print a return, we will need to break the statement into two pieces, one to print the value of the hypotenuse and the other to provide the return.

Modify your source program so that hypotenuse is displayed with precision 3. Then test the correctness of your modification, continuing when Hypot works as intended.

 

Phrases you should now understand:

Design, Behavior, Object, Operation, Algorithm, Coding, Testing, Debugging, Maintenance, Specification, Stepwise Translation, Source Program, Executable Program, Input Statement, Output Statement.


 

Submit:

Turn in to your instructor a hard copy of your final version of Hypot.java, and a hard copy of its output given the input values 1 and 1.

 

Clean Up

Don't forget to clean up. Among other tasks you may need to save your work to a floppy, remove your originals from the hard drive, etc.


Back to This Lab's Table of Contents

Back to the Prelab Questions

Forward to the Homework Projects


Back to the Table of Contents

Back to the Introduction


Copyright 2000 by Prentice Hall. All rights reserved.