Lab 1: Software Engineering


Introduction

Our exercise 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: Translate your algorithm into a programming language like C++.
  3. Testing: Run your program with sample data to check for errors, and debug your errors until there aren't any more.
  4. Maintenance: Perform any modifications needed to improve the program.
This lab will use an example problem to elaborate a bit on these steps and to see an example C++ program. The main work you'll have to do for this lab will be modifying the program and observing the results.

Do not worry or panic if a topic is confusing and it's not explained in great detail. We will go through each of these topics in subsequent labs.

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 C++ program. This is done by spending some time designing your program.

Helpful hint: 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.

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 can describe our program's behavior 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. It does not magically fall from the sky, but it's a natural explanation of what we want the program to do. What are the steps you would do to solve the problem? We then throw in steps to read in data and to display the results of the solution.

Once we have our behavior description, we can pick out our objects and operations. True to its name, OCD says we pick out our objects first...

Objects

Picking out our objects is as simple as picking out the nouns in our behavioral description (ignoring nouns like "program" and "user"). The result is a list of the objects our program needs to define:

Description Type Kind Name
the screen ostream varying cout
a greeting string constant --
a prompt for the legs string constant --
the length of the first leg double varying leg1
the length of the second leg double varying leg2
the keyboard istream varying cin
the hypotenuse value double varying hypotenuse
labels for the hypotenuse string constant --

Once we know the objects in our problem, it is useful to specify the basic flow of information in our program in terms of the objects going in and data going out. 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

Just as our nouns are objects, verbs are our operations for our algorithm:

Description Predefined? Name Library
display the greeting yes << iostream
display a prompt yes << iostream
read the length of the first leg yes >> iostream
read the length of the second leg yes >> iostream
compute the hypotenuse length ?? ?? ??
display the hypotenuse yes << iostream
display the label yes << iostream

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.

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 Library
compute the square of a leg yes pow() cmath
add two squared legs yes + built-in
compute the square root yes sqrt() cmath
store a value in a variable yes = built-in

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---an algorithm. 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 C++.

We've already noted above some C++ objects that we'll use (specifically cin and cout) and some C++ operations (e.g., >> and <<). We'll cheat a bit in our algorithm by using these C++ names, but if we wanted to switch languages, we could easily switch these names.

  1. Display via cout a greeting for the user.
  2. Display via cout prompts for the two leg lengths.
  3. Read values from cin for 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 cout the value of hypotenuse along with an appropriate label.

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

Coding

Before working through the rest of this lab, make sure you have work through the operating-system and compiler specific portions of Lab #0 to become familiar with your environment.

In the lab1 directory, open up the hypot.cpp file. If you obtained this from a zipped file, you should find the file in the lab1 directory; otherwise, you'll have to create the directory and download the file.

We are going to take a big jump here and simply look at the code for this program; in most labs, you'll write a lot of this code. Take a look at the hypot.cpp file that's provided for you.

You'll play around with the syntax of this program below, but for now compare how the program statements match up with the algorithm steps. For each step of the algorithm, there's usually one and occasionally two C++ statements for the algorithm step. This indicates you have a good algorithm; if you find yourself needing more statements for an algorithm step, then you probably have to revise your algorithm.

After the program is written, we compile the program. Go ahead and do this. (You will not know how to do this unless you've read the appropriate OS and compiler directions from Lab #0.)

In practice, it is best to compile the program on a regular basis, as you write it, so that compiler errors don't overwhelm you at the end of the coding. It will make finding the errors easier. But since this program was given to you, you can move abnormally fast through this step.

Testing

The third stage of program development is a thorough testing. The basic idea is to execute the program using sample data values that test the program, to see if it contains any logic errors.

You compiled the program in the previous section, so execute the program with the following values, to see if you get the correct results.

predicted observed
  leg1     leg2     hypotenuse     hypotenuse  
1.0 1.0 1.414214  
3.0 4.0 5.0  
5.0 12.0 13.0  

Question #1.1: Write down the observed values that you get testing these inputs on the program you compiled.

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.

Question #1.2: What changes might you like to make to this program? (You won't have to implement them, so let the sky be your limit!)

Playing with the Program

Load hypot.cpp into your editor.

First of all, you will be making changes to the program, so you should add a modification comment in the comment at the top of the file. Add something like this:

* Modification history:
*    by John VanDoe in September 2002 for CPSC 185 at Calvin College
*       Modified to run the experiments for Lab #1.

This should go right after the author information. Recompile and execute the program. It shouldn't execute any differently. That's because that text at the beginning of the document are all comments. But you might wonder what makes a comment.

Comments

A comment is text that's useful for the programmer. The compiler will ignore it completely. So if you type junk into a comment, the compiler will ignore it. If you type junk outside of a comment, the compiler will take it as program code and will get confused.

For each of the following questions, make the change suggested and recompile your program. If it compiles okay, we'll assume it runs okay; your answer for the question can be "compiles fine". But if a change generates a compiler error, your answer for the question should be the first error message that the compiler gives you. Always undo your change before going on to the next question.

Question #1.3: What happens when you remove the asterisks (i.e., *) before the lines you just added?

Undo your change.

Question #1.4: What happens when you remove the /* at the very beginning of the file?

Undo your change.

Question #1.5: What happens when you remove just the / at the very beginning of the file?

Undo your change.

Question #1.6: What happens when you remove just the first * (right after the / at the beginning of the file?

Undo your change.

You should have a good idea where this comment starts.

Question #1.7: Where do you suppose this opening comment ends?

Another way to introduce a comment is with //, two slashes. This type of comment is used to indicate the algorithm steps in the program.

Question #1.8: Remove one of the //s in the program. What happens when you compile the code?

And, yet again, make sure your program is restored back to a compilable state.

Includes

You'll find two lines that use the #include directive right after the opening comment. These lines tell the compiler that it needs to access some library files. They're necessary for some of the operations that the program does.

Question #1.9: What happens when you delete one of the #include lines?

Question #1.10: What happens when you add some spaces before a #include?

Question #1.11: What happens when you move one of the includes to the end of the file?

Remember to restore your file after each question.

The Program: int main()

The main program is designated by main(). The main algorithm must be encoded in this function.

Question #1.12: What happens when you drop the parentheses: main() becomes just main?

Question #1.13: What happens when you drop the int before main()?

Question #1.14: What happens when you replace the curly braces (i.e., { and }) with parentheses (i.e., ( and ))?

Question #1.15: What happens when you add extra spaces before the int?

Again, restore your program after each question so that it compiles and executes correctly.

Input and Output

The input and output statements for Steps 1, 2, 3, and 5 all begin with either cin or cout followed by objects that should be displayed and used to read in values. The objects are separated with the operators << and >>.

Question #1.16: What happens when you replace a cout with cin?

Question #1.17: What happens when you remove a >>?

Question #1.18: What happens when you remove a <<?

Question #1.19: What happens when you replace a >> with <<?

Question #1.20: What happens when you replace a << with >>?

And yet again, restore your program after each question so that it compiles and executes correctly.

Declarations

Before we can use a variable in a C++ program, we must first declare it. The leg1 and leg2 variables are declared with this line:

double leg1, leg2;

Question #1.21: What happens when you delete this line?

Question #1.22: What happens when you move it after Step 4?

Question #1.23: What happens when you move it before main()?

By this point, you've probably run across a change or two that didn't matter. You made the change, the program still compiled, and the program still executed correctly. However, not all of these changes are good. You'll see again and again that there's often many ways to write your program, many of them bad ways.

Helpful hint: Pay careful attention to the way code is written in the examples that you see in this lab manual. Try to mimic this as closely as you can even if something else works just as well.

Be sure to return your file to this original state.

Semicolons

Semicolons end almost every C++ statement.

Question #1.24: What happens when you remove a semicolon from one of the statements?

Question #1.25: What happens when you add an extra semicolon?

Question #1.26: What happens when you add some extra spaces before a semicolon?

You might be a bit worried about the #include directive; it doesn't have a semicolon!

Question #1.27: What happens when you add a semicolon after an #include directive on the same line?

Question #1.28: How do statements and directives differ with respect to semicolons?

Submit

Turn in your answers to the questions in this lab exercise.

Terminology

algorithm, behavior, behavioral description, coding, design, implement, maintenance, object, object-centered design, OCD, operation, program maintenance, software engineering, specification, testing
Lab Home Page | Prelab Questions | Homework Projects
© 2003 by Prentice Hall. All rights reserved.
Report all errors to Jeremy D. Frens.