CS245 - Comp Sci II



Chapter 7 - Process I: Organizing and Controlling Classes



7.1 SOFTWARE ENGINEERING


Edsger Dijkstra - "GOTOs Considered Harmful"
"Programs are written for people as well as for computers"
Software Engineering - analysis and application of strategies to design, construct, and maintain large-scale software systems
Uses automated tools for recording and tracting system requirements and for performing system analysis
Software metrics - formal measures of software quality

7.2 The Traditional Software Life Cycle


Specification
	|
	v
    Design
	|
	v
Implementation
	|
	v
  Testing
	|
	v
Maintenance

SPECIFICATION
A detailed description of the essential properties of an object. In programming, a complete description of a program unit's required behavior.
Inputs & Outputs

Result - document detailing all decisions made about the program's requirements
DESIGN
IMPLEMENTATION (programming)
An object built to satisfy a specification. In programming, a program unit written to behave as specified. Not all behavior is specified, which gives the implementor some flexibility in deciding implementation specific behavior. ("Design with style")
TESTING

MAINTENANCE

7.3 The Declare-Define-Use Approach, Revisited
Specification

Start | v High-level Design
Identify a Class | v Choose a Class | v Low-level Design
Declare a Class ^ | v Implementation
Define a Class ^ | v Testing
Use a Class | v Integrate a Class | v Maintenance
Write main

DDU is highly iterative consisting of repetitive design-implement-test-maintain cycles
As classes are individually declared, defined and tested, they are integrated and retested globally
NOTE: main written last
RULES OF THUMB:
1) Nouns from the program spec are usually implemented as classes while verbs are the methods (functions)
2) When in doubt, refer to reality
3) Don't be afraid to redesign your classes
To declare a class, you write its header file (.H).
To define a class, you write its implementation file (.CPP).
To use a class, you write a driver program that tests the class as completely as possible. The time you spend testing classes individually will more than pay for itself when it comes to testing your complete program.
To integrate a class, you write a driver program that exercises the class in the context of other classes that refer to it.

7.4 Files, Linkage, and the DDU


Linking - process where distinct object files are combined into a single working program
contents of files must be consistent with each other
internal linkage - identifiers are accessible only from within the file in which they are declared (static)
by default - local variables, constant identifiers, type names (except classes), inline functions
external linkage - accessible to all files (extern)
by default - global variables, predefined functions, class names
typedefs, function parameters have no linkage
multiple declarations for an identifier must be "type-safe linkage", i.e. must describe the same kind of entity and same type information
can "include" multiple copies of the same header file: #include <iostream.h>
error if contains definitions of global constants
requires conditional compilation:
#ifndef _IOSTREAM_H
#define _IOSTREAM_H
:
#endif

7.5 PIP: Riding the Elevator


Preliminaries: Program Specifications
Simulation of an automatic elevator
a) Inputs: number of riders randomly placed at random floors; floor numbers by keyboard
b) Outputs: Which floor elevator is on; direction it is heading; when riders enter and leave elevator
c) Constraints: riders may call from any floor and exit any floor; elevator is idle until called; operates until all requests completed; must board elevator when requested and and must disembark when floor is reached; no limit on number of passengers
STEP 1: IDENTIFY CLASSES
elevator, rider
STEP 2: CHOOSE A CLASS
elevator
STEP 3: DECLARE THE CLASS ELEVATOR
PublicElevator
ButtonsPushed
MoveToNextFloor
DisplayStatus
floorButtons[FLOORSINBLDG+1]
int currentFloor
going
Create and initialize elevator
Are any floor buttons pushed?
Moves the elevator
Tell where the elevator is
One button per floor (ON / OFF)
Number of floor elevator is on
Direction indicator (UP / DOWN)
Private


ChooseMove



Determine floor to move to

// ­­­­­­­­­­ELEVATE.H­­­­­­­­­­
// Declaration file for class Elevator
#ifndef _ELEVATE_H
#define _ELEVATE_H
const int FLOORSINBLDG = 10; // predetermine the number of floors

enum Direction {DOWN,UP}; // possible directions for the elevator
enum Button {OFF,ON}; // each floor button can be on or off

class Elevator
{
// Since nearly all of Rider's member functions need access to the private data of Elevator
// we make the entire Rider class a friend of Elevator friend class Rider;

public:
Elevator(); // builds a new elevator on ground floor
int ButtonsPushed(); // are there any floor buttons ON?
void MoveToNextFloor(); // moves elevator
Button floorButtons[FLOORSINBLDG+1]; // the collection of floor buttons
int currentFloor; // where elevator is now
Direction going; // which direction is it heading
void DisplayStatus(); // tells us where elevator is

private:
int ChooseMove(); // private function to determine which
// floor to move to next
};
#endif

STEP 4: DEFINE THE CLASS ELEVATOR
// ­­­­­­­­­­ELEVATE.CPP­­­­­­­­­­
// Definition file for class Elevator

#include <iostream.h> // for cin, cout
#include "ELEVATE.H" // for Elevator declarations
#include "UTILITY.H" // for WaitForUser
Elevator::Elevator()
// This function creates a new elevator, places it at the ground
// floor, directs it up, and turns all of its buttons off.
{
currentFloor=1;
going=UP;
for (int i=0; i<FLOORSINBLDG+1; i++) // for each button
floorButtons[i]=OFF; // turn it off
}
int Elevator::ButtonsPushed()
// This function returns 1 if any of the elevator's floor
// buttons are still on. Otherwise, it returns 0.
{
for(int i=1; i<FLOORSINBLDG+1; i++) // check all buttons
if (floorButtons[i]==ON) return 1; // if any one is on, return true
return 0; // else, return false
}

int Elevator::ChooseMove()
// This function determines which floor to move from, as follows. It trys to move to the closest floor
// that needs service,in the same direction as the elevator is currently headed. If no floors need
// service in the same direction, it "turns around", and trys again.
{
for(int t=1; t<=2; t++) // loop twice to change direction
{
if (going==UP) // try moving up
{ // check to see if any higher floors need service
for (int i=currentFloor;i<FLOORSINBLDG+1; i++)
if (floorButtons[i]==ON) return i;
going=DOWN; // if nothing up, head down
continue; // and try again
}
else // try moving down
{ // check to see if any lower floors need service
for (int i=currentFloor; i>0 ; i­­)
if (floorButtons[i]==ON) return i;
going=UP; // if nothing down, head up
continue; // and try again
}
}
return ­1; // if no moves possible, return ­1
}
void Elevator::MoveToNextFloor()
// This function moves the elevator to the closest floor requiring service
{
int newFloor=ChooseMove(); // determine which floor to move to
if (newFloor==­1) // if no moves needed
cout << "Elevator stopped at floor " << currentFloor;
else
{ // if a move is possible
currentFloor=newFloor; // change to new floor
floorButtons[currentFloor]=OFF; // turn that floor's light off
DisplayStatus();
}
}

void Elevator::DisplayStatus()
// This function displays the current status of the elevator on the screen
{
cout << "\nElevator is currently stopped at floor " << currentFloor << " heading ";
if (going==UP)
cout << "up\n";
else
cout << "down\n";
WaitForUser();
}

STEP 5: USE THE CLASS ELEVATOR

// ­­­­­­­­­­ DRIVER1.CPP ­­­­­­­­­­
// Test program to exercise classes Elevator
#include <iostream.h> // for cin, cout
#include "ELEVATE1.H" // for Terminate
void main()
{
Elevator e1; // create a new elevator
e1.DisplayStatus();
e1.floorButtons[4] = ON; // turn some of the floor buttons on manually
e1.floorButtons[7] = ON;
//see which floor it would choose, move there and turn off button
e1.MoveToNextFloor(); // move to a floor requiring service

e1.floorButtons[2] = ON; //turn on another button to present elevator with a choice

//move to next floor, turn off button, make sure it choose the right floor
e1.MoveToNextFloor();

//move to final floor and turn off that button
e1.MoveToNextFloor();

cout << "\nWe made it!!!\n";
}

STEP 6: DECLARE THE CLASS RIDER
PublicRider
GetOn
GetOff
PushButton
SelectFloor
Respond
Create a new rider
Getting on an elevator
Getting off an elevator
Push any of the elevator's buttons
Choose a floor once on elevator
Decide whether to get on or off
Privatestatus

startFloor
destinationFloor
number
WAITING, ABOARD, or SERVED

Floor from which service requested
Rider's destination
A rider ID

When in doubt, declare member data as private and member functions as public. You can always revise your declarations later.

//­­­­­­­­­­RIDER.H­­­­­­­­­­
// Declaration for an elevator rider.

#ifndef _RIDER_H
#define _RIDER_H
#include "ELEVATE.H" // for class Elevator

enum Status {WAITING,ABOARD,SERVED}; // a rider is in one of these states

class Rider {
public:
Rider(int num, Elevator* e, int start=1); // builds a new rider
void Respond(Elevator* e); // the user responds to where the elevator is
Status s; // the rider's state
void GetOn(Elevator* e); // getting on the elevator
void GetOff(Elevator* e); // getting off of the elevator
void PushButton(Elevator* e, int thisFloor); // to hail elevator
void SelectFloor(Elevator* e); // to choose floor once on elevator
private:
int startFloor; // the floor from which service was requested
int destinationFloor; // the rider's destination
int number; // a rider number, for id purposes only
};
#endif
STEP 7: DEFINE THE CLASS RIDER

//­­­­­­­­­­RIDER.CPP­­­­­­­­­­
// Definition file for an elevator rider.

#include <iostream.h>
#include "RIDER.H"

Rider::Rider(int num, Elevator* e, int start)
// This function creates a new rider for a particular elevator. The rider is placed at a floor, pushes
// the button on that floor to request service, is assigned an identifying number, and is
// given a chance to respond to where the elevator is.
{
s = WAITING;
startFloor = start;
number = num;
PushButton(e, startFloor);
cout << "\n\nPassenger " << number << " is waiting at floor " << startFloor << '\n';
Respond(e);
}

void Rider::Respond(Elevator* e)
// This function embodies the riders response to where the elevator is. It allows the rider to get on of
// get off the elevator, if the location of the elevator is relevant to the rider.
{
if ((s==WAITING) && (e­>currentFloor==startFloor))
{ // if the rider is waiting, and the elevator arrives
GetOn(e);
return;
} // if the rider is on the elevator, and arrives at destination
if ((s==ABOARD) && (e­>currentFloor==destinationFloor))
{
GetOff(e);
return;
}
}

void Rider::GetOn(Elevator* e)
// This function simulates the rider getting onto the elevator.
// The rider's status changes, and the rider "pushes" a floor button
{
s = ABOARD;
cout << "\nPassenger " << number << " enters elevator at floor " << e­>currentFloor << '\n';
SelectFloor(e);
}

void Rider::GetOff(Elevator* e)
// This function simulates the rider getting off of the elevator.
{
s = SERVED;
cout << "\nPassenger " << number << " leaves elevator at floor " << e­>currentFloor << '\n';
}

void Rider::PushButton(Elevator* e, int thisFloor)
// This function simulates a rider request for service, either by
// pushing a button on a floor, or by pushing a button in the elevator.
{
e­>floorButtons[thisFloor]=ON;
}

void Rider::SelectFloor(Elevator* e)
// This function simulates the rider pushing a floor button
// from inside of the elevator
{
cout << "\nTravelling to which floor (1­" << FLOORSINBLDG << ") ? ";
cin >> destinationFloor;
PushButton(e, destinationFloor);
}

STEP 8: USE AND INTEGRATE THE CLASS RIDER

// ­­­­­­­­­­ PIP7.CPP ­­­­­­­­­­
// Test program to exercise classes Elevator and Rider.
#include <iostream.h> // for cin, cout
#include <stdlib.h> // for rand, srand
#include <time.h> // for clock
#include "RIDER.H" // for class Rider
#include "UTILITY.H" // for Terminate
void main()
{
Elevator e1; // create a new elevator
// iniitalize random number generator, so we can use it below
int s = int(clock());
srand(s);
int aFloor;
// create 4 randomly placed riders
aFloor=((rand() % FLOORSINBLDG) + 1);
Rider r1(1, &e1, aFloor);
aFloor=((rand() % FLOORSINBLDG) + 1);
Rider r2(2, &e1, aFloor);
aFloor=((rand() % FLOORSINBLDG) + 1);
Rider r3(3, &e1, aFloor);
aFloor=((rand() % FLOORSINBLDG) + 1);
Rider r4(4, &e1, aFloor);
// process all requests for service from riders
while (e1.ButtonsPushed()) // continue as long as buttons still on
{
e1.MoveToNextFloor(); // move to a floor requiring service
r1.Respond(&e1); // give each rider a chance to get
r2.Respond(&e1); // on or off the elevator
r3.Respond(&e1);
r4.Respond(&e1);
}
// Make sure all requests have been satisfied
cout << "\nRider 1 status: " << (int) r1.s;
cout << "\nRider 2 status: " << (int) r2.s;
cout << "\nRider 3 status: " << (int) r3.s;
cout << "\nRider 4 status: " << (int) r4.s;
cout << "\n\nElevator returned to ground floor having";
cout << "\nprocessed all requests for service.";
Terminate();
}
STEP 9: RECONSIDERING SOME DECISIONS
1) Change Elevator's data members to private
a) don't want data members accessible to other objects
b) make explicit relationship between Rider and Elevator by making Rider a friend which allows Rider to access private data and function members
2) Change Rider's member function to private and status to public
3) Use WaitForUser and Terminate from UTILITY.H

Exercises