Lab 1: Software Engineering
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:
-
Design: Carefully plan your program using OCD...
-
Behavior:
Describe as precisely as possible what the program must do;
-
Objects:
Using the behavioral description,
identify the objects needed to solve the problem;
-
Operations:
Using the behavioral description,
identify the operations needed to solve the problem;
-
Algorithm:
Organize the objects and operations into an algorithm
-- a sequence of steps that solves the problem;
-
Coding: Translating your algorithm into a programming language
like C++;
-
Testing:
Running your program with sample data to check for errors,
and debugging those errors until it can be deemed correct; and
-
Maintanence:
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 C++ 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 particulare, 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 |
ostream |
varying |
cout |
| 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 |
istream |
varying |
cin |
| the hypotenuse value |
double |
varying |
hypotenuse |
| labels for the hypotenuse |
string |
constant |
-- |
For each noun, we have described some of its attributes:
-
its basic description;
-
its type, so far as C++ is concerned
(we'll be learning more about these in Labs 3 and 4);
-
whether its value varies or remains constant
as the program executes; and
-
how we will refer to that object within our program
(or -- if it needs no name).
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 |
Library? |
display a string (the greeting) |
yes |
<< |
iostream |
display a string (the prompt) |
yes |
<< |
iostream |
read a double (the first leg value) |
yes |
>> |
iostream |
read a double (the second leg value) |
yes |
>> |
iostream |
| compute the hypotenuse length |
?? |
?? |
?? |
display a double (the hypotenuse) |
yes |
<< |
iostream |
display a string (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.
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 |
Library? |
find the square of a double value |
yes |
pow() |
cmath |
add two double values |
yes |
+ |
built-in |
find the square root of a double value |
yes |
sqrt() |
cmath |
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 function and/or library 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 C++.
For our problem, we can organize the objects and operations
as follows:
-
Display via
cout a greeting for the user.
-
Display via
cout prompts for the two leg lengths.
-
Read from
cin two double values,
storing them in leg1 and leg2.
-
Compute the hypotenuse length using the Pythagorean Theorem:
hypotenuse = sqrt(pow(leg1, 2.0) + pow(leg2, 2.0))
-
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
Begin by creating a new directory or folder for this exercise
(e.g., 1)
and then save a copy of hypot.cpp in this directory.
(Your instructor may have a local version for you to copy.)
hypot.cpp contains just a fragmentary opening comment
(documentation that explains what the program does, who wrote it, and so on),
so start by using your text editor and customizing this 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 C++.
We can begin this task by adding a minimal C++ program to hypot.cpp
after the opening comment:
int main()
true
Every C++ program 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.cpp,
translate your program into machine language, as we did in Lab 1.
If your compiler is ANSI-compliant, 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 C++ 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.
Note:If a warning message like:
Warning: No return statement at end of function
or something similar is displayed, then your compiler is not ANSI-compliant.
If this happens, add the line
return 0;
after the { and before the } in the main function.
This causes your main function to terminate and return the integer zero,
which signals "normal termination" to many operating systems.
Once we have the minimal C++ 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 C++ 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 cout a greeting for the user.
This step can be performed using a C++ output statement.
In general, to display a sequence of values Value1...
ValueN on the screen, we can use the following
output statement pattern:
cout << Value1 << Value2 << ... << ValueN;
Add an output statement to your source program following the {
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, re-compile it.
The compiler should generate an error message like the following:
hypot.cpp: In function int main()
hypot.cpp: 'cout' undeclared...
The problem is that the meanings of the symbols cout and <<
are declared in a system file named iostream, not in your program.
To access these declarations from that file, we can instruct the compiler to
insert the contents of iostream into hypot.cpp,
by adding an #include directive just before the int main()
line:
#include<iostream>
using namespace std;
This #include directive must appear in any program that
performs output using cout and <<.
The # symbol should be the first column,
in order for it be processed correctly.
The <> symbols around the file name tell the compiler to
look for iostream in a special system "include" directory,
rather than in your current directory.
The using namespace std; directive tells the compiler
that we will be using standard C++ naming conventions,
which will be the case throughout this lab manual.
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 hypot 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 C++ 'sentence', but it 'says' something
different than you intended.)
Compare your source program statements against the output produced by
hypot, 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 cout prompts for the two leg lengths.
Like our first step, this step can also be accomplished using an output
statement.
Add to hypot.cpp 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 cin two double values,
storing them in leg1 and leg2.
This step can be performed using a C++ input statement.
In general, to read a sequence of values from the keyboard
and store them in Var1...VarN,
we can use the following input statement pattern:
cin >> Var1 >> Var2 >> ... >> VarN;
Using this pattern, add an input statement to hypot.cpp
to perform step 3 of our algorithm.
Then check its correctness by retranslating your program.
Similar (but different) errors should be generated,
but this time it is our objects that are undefined.
C++ requires that an object be declared before it can be used,
using a declaration statement whose pattern is:
Type Var1,
Var2,
...
VarN;
where Type is the types of the objects being declared,
and Var1, ..., VarN are the names of those objects,
as specified back in our Design stage.
In C++, the type double is used to define objects capable of storing
real numeric values.
One of the nice features of C++ is that declarations are statements,
and thus can appear anywhere a C++ statement is permitted.
Insert a declaration for leg1 and leg2 just before
your input statement.
Then re-translate 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() functions that perform
square root and exponentiation, respectively.
hypotenuse = sqrt(pow(leg1, 2.0) + pow(leg2, 2.0))
As noted previously, the sqrt() and pow() functions are
declared in the library file cmath,
so add a #include directive before your main function
to insert the contents of cmath into hypot.cpp.
(If your compiler is not fully ANSI-compliant, you may have to use
math.h instead of cmath
To perform this step, we can use a variant on the declaration statement
that allows us to declare and initialize an object.
Since hypotenuse is to store a double value,
we write:
double hypotenuse = sqrt(pow(leg1, 2.0) + 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 cout the value of hypotenuse.
along with an appropriate label.
This final step of our algorithm is another output statement.
Since the pattern for the output statement allows us to output
multiple values:
cout << Value1 << Value2 << ... << ValueN;
a single output statement is sufficient to accomplish this step.
Add an output statement to hypot.cpp, such that if the value
of hypotenuse is 1.414214, then the message
--> The hypotenuse length is: 1.414214
is displayed with blank lines above and below it (to make the result
stand out).
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 must alter the default precision.
This can be done with an I/O manipulator called setprecision()
(declared in the file iomanip) whose general form is:
setprecision( IntegerExpression )
A manipulator can be inserted into cout just like a value.
That is, if setprecision() is placed in an output statement:
cout << Val1 << setprecision(i) << Val2 << ... << ValN;
then real values before it (i.e., Val1 ) will be displayed
with the default precision, but real values following it
(i.e., Val2 << ... << ValN)
will be displayed with precision i.
Modify your source program so that hypotenuse is displayed
with precision 3.
Don't forget to #include the file iomanip
before the int main() line!
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, I/O Manipulator.
Submit:
Run turnin on dogbert for hypot.
|
|
|
|
Home
Help
Lab 0
Lab 1
Lab 2
Lab 3a
Lab 3b
Lab 4
Lab 5
Lab 6
Lab 7
Lab 8
Lab 9
Lab 10
Lab 11
Lab 12
Lab 13
Membership
Login |