Reading Interfaces and Writing Client Code
This set of Mini-Lab Exercises is the first in a series in which students build a small program with several fish moving around in an aquarium. The set includes the following exercises:
Each section contains an Introduction to a problem or task, (usually) abridged versions of one or more Patterns that will be useful in solving the problem or completing the task, and an Exercise.
Before working through this Mini-Lab,
students should understand the role of the main
function in an application and the role of classes and objects.
Students should also have read over the patterns that appear in this
document before the lab.
main
function that runs the simulation.
There is also an Aquarium
class and an
AquaFish
class. The skeleton program constructs an
Aquarium
object in the main
function;
in the exercises that follow, you will be
creating several AquaFish
objects in the aquarium and
directing them to move.
The program also contains a
Display
class. There are actually two different
implementations of the Display
class. One produces a
text-based display of the fish in the aquarium; the other draws them
graphically using the CMU Graphics package*.
The main
function constructs a
single Display
object that you will use to display the
aquarium after you have created and moved the fish. Finally, the
program includes some utility classes and functions, including the
RandGen
class from the Advanced Placement Marine Biology
Case Study+ for generating random numbers, and the
WaitNClear
function, which is useful if you are using the
graphical implementation of the Display
class.
Exercise
|
main
function
creates an aquarium, but it doesn't have any fish in it. We can use
the Declare-Construct-Initialize pattern to create
fish to go
in the aquarium. The AquaFish
interface gives the
specifics on how to construct objects of the class.
Therefore, you should specify the type of the new variable (declare), set aside space for it in memory (construct), and (usually) initialize it to a meaningful value before you use it in your program. Give the variable an Intention Revealing Name to make your code easier to read, understand, and maintain. If the variable is an object of a class, Read the Interface for Constructors to determine how to construct and initialize the variable.
In C++, a
variable definition declares the type of an object and sets
aside the memory for it (constructs it) simultaneously. For
fundamental types (such as char
, int
,
and double
), the variable will not be initialized unless you
do it explicitly, as in the second example below.
Class objects are always initialized by a constructor function,
which is invoked automatically by the program when you construct an
object.
A constructor may take parameters to initialize the object, in which
case you should list the parameters in parentheses when you construct
the object. A default constructor takes no parameters, in
which case you do not need (and should not have) any parentheses when you
construct the object.
int i; // constructs an integer (uninitialized)
int j = 0; // constructs an integer, initializing to 0
MyClass newObject; // constructs a MyClass object using default constructor
MyClass newObject(initValue); // constructs a MyClass object using constructor with one parameter
For any given class,
Read the Interface for Constructors to determine
how to construct and initialize objects of the class.
Therefore, all of the names in your programs (variables and parameters, classes, member functions) should denote the purpose of the item they refer to. Variables, for example, should be named for the objects they represent.
The Intention Revealing Name pattern is described more fully in Bergin's Coding at the Lowest Level: Coding Patterns for Java Beginners. The pattern comes from Kent Beck's Smalltalk Best Practice Patterns (Prentice Hall, 1997).
Therefore, read the constructor declaration(s) in the class interface to determine how to construct and initialize, if necessary, the object that you need. Concentrate on the constructors that are accessible to you; for example, if you are writing client code, read the public constructor declarations.
A constructor declaration will tell you
Reading a constructor or member function declaration and then writing code that satisfies the declaration is tricky, especially for novices. This is because the client code you write will not look exactly like the declaration. Think of the declaration as being like a dictionary entry. Imagine that you want to use the word "cat" in a sentence, but you're not sure of its meaning. You look it up in a dictionary and find the following.
Let's look at an example from the Aquarium class. In this class, there are two constructors. How do we know? A constructor declaration looks like a member function whose name is the same as the class. There are two "member functions" called Aquarium in the Aquarium class interface.
Aquarium()
// postcondition: width and height are initialized to 0
// (there are no valid locations in such an aquarium)
Aquarium(int width, int height);
// postcondition: Width() == width, Height() == height
Since both are in the public
part of the class interface,
we can use either one to construct an aquarium in client code.
The first declaration tells us that there is a default constructor for
an Aquarium, but that it has no valid locations. We could
construct a default Aquarium as follows:
Aquarium myAquarium;
The second declaration tells us that there is also a constructor that
allows us to initialize the Aquarium object to a specified height and
width.
When we construct an Aquarium object using this constructor we do not need to
specify that it is public nor what the types of its parameters are,
anymore than we have to specify that a cat is a noun when we use it in
a sentence. We do, however, need to provide values for the two
parameters. Thus, we could construct a 600 x 400 Aquarium as follows:
Aquarium myAquarium(600, 400);
Exercise
main function in the aquarium program to
construct three fish variables.
|
Therefore, read the member function declarations in the class interface to determine how to invoke member functions for an object of the class (also known as "sending a message" to the object).
A member function declaration will tell you
Let's look at two
examples from the Aquarium class:
Width
and ValidLoc
.
int Width() const;
// returns width of aquarium
bool ValidLoc(int x_coord, int y_coord) const;
// returns true if and only x_coord and y_coord represent a
// valid location inside the aquarium
What can we learn from these declarations?
Both member functions are public, so we may use them in client code. Both
have return values (Width
returns an
integer value; ValidLoc
returns a
boolean value), so we should capture the value returned in a
variable or embed the member function call in a larger expression.
The
Width
member function does not take any parameters and returns an
int
.
The ValidLoc
member function requires two parameters and returns a
boolean value. Thus, this member function may be used in a logical
(true
/false
) expression. Both functions are
const member functions (indicated by the const
keyword at the end of the function declaration), so neither function
modifies its Aquarium object.
How can we use this knowledge?
If y
is a well-defined integer value
(see the Declare-Construct-Initialize pattern),
then the following are valid examples of these two member functions.
int aquariumWidth = myAquarium.Width();
if ( myAquarium.ValidLoc(myAquarium.Width() / 2, y) )
...
As with constructors, you do not need to specify the type of the
parameters as you pass them (see the Read the Interface for
Constructors pattern). Nor do you specify the return type of the
member function as you call it.
A member function with a void
return type does not return any value
to the function that called it. Instead, it usually modifies its object,
produces output, or changes the state of the program in some other
way. It may or may not require parameters.
Here is a declaration of a void
member function from the
Display class.
void ShowAquarium();
// postcondition: aquarium passed to constructor has been displayed
Since ShowAquarium
does not return anything, it cannot be
embedded in an expression or an assignment statement. A
void
function call is a statement on its own. For example,
theDisplay.ShowAquarium();
Exercise
|
*The CMU Graphics Library was created by the Carnegie Mellon University School of Computer Science and is used in their introductory programming courses. It is available from Mark Stehlik's Advanced Placement page.
+The Aquarium series of labs is loosely based on the Advanced Placement Computer Science Marine Biology Simulation Case Study, available from the College Board for face-to-face teaching purposes.