Section 8 - Completion of the OOP Project

7A.8.1 What Goes Into main()?

Any details of the output or processing that are associated with the sample main must stay out of the class, and instead be done in main() or in non-Patient methods that main() calls. The user interaction, for example, does not go into the Patient class. This would not make sense, since virtually no one but us, in our testing, will ever use these methods.  Only put methods in the Patient class that are likely to be used by others and that pertain to the basic Patient data.

7A.8.2 Test main() Without the Patient Class - The Naked Main

We will do main() in two steps. First, lets' get the UI (User Interface) working.

We'll call this the Naked Main phase, since it involves writing a main() without any of the target class equipment included.   This is only about establishing that we are getting good data from the user.  This phase will look very different for every student, and I probably won't require that you show me much of this. 

Designing the Naked Main for the current example is straightforward.  Note that we will be mixing strings and numbers during input. The best way to do this is by using getline() for everything and converting the id and temperature to numeric data types as needed.  We don't want to mix getline() with cin>>variable.  Review the relevant modules if you forgot this syntax.

// ---------------- SOURCE -------------------------
#include <iostream>
#include <string>
#include <sstream>
using namespace std;

// method prototypes
string getPatientName();
int getPatientID();
double getPatientTemp();

int main()
{
// variables to capture input
string userName, stringIn;
double userTemp;
int userId;

// get the info for patient #1:
cout << "Patient #1 ---\n";

// we built three helper methods for this
userName = getPatientName();
userId = getPatientID();
userTemp = getPatientTemp();

// testing that we got the correct input:
cout << "Patient #1: " << userName << " id: " << userId
<< " temp: " << userTemp << endl << endl;

// get the info for patient #2:
cout << "Patient #2 ---\n";

// we built three helper methods for this
userName = getPatientName();
userId = getPatientID();
userTemp = getPatientTemp();

// testing that we got the correct input:
cout << "Patient #2: " << userName << " id: " << userId
<< " temp: " << userTemp << endl << endl;
return 0;
}

// main methods (non-Patient methods)
string getPatientName()
{
string stringIn;
cout << "What's the patient's name? ";
getline(cin, stringIn);
return stringIn;
}

int getPatientID()
{
int id;
string stringIn;

cout << "What's the patient's id #? ";
getline(cin, stringIn);
istringstream(stringIn) >> id;
return id;
}

double getPatientTemp()
{
double temp;
string stringIn;

cout << "What's the patient's temperature? ";
getline(cin, stringIn);
istringstream(stringIn) >> temp;
return temp;
}

/* ------------- RUN (OUTPUT) ----------------------
Patient #1 ---
What's the patient's name? Marsha Malone
What's the patient's id #? 11111
What's the patient's temperature? 98.5
Patient #1: Marsha Malone id: 11111 temp: 98.5

Patient #2 ---
What's the patient's name? Frank Lewis
What's the patient's id #? 9876
What's the patient's temperature? 22.5
Patient #2: Frank Lewis id: 9876 temp: 22.5
--------------- END OF RUN ------------------------- */

Note that we haven't done any error checking.  This will be handled through the mutators.  Right now we are only confirming that we got the correct user input.  This is a very important step in interactive main() methods. Don't try to write lots of code without first doing a main() like you see above.  Now we know we can trust the information we are getting from the user.

7A.8.3 Combining the Real main() with the Target Class - Synthesis

We will proceed slowly. Next step is to take the data we received and place it into Patient objects.  We will delay doing the last part of logic (testing to see which patient is sicker) until we have this portion ready.  This is called the Synthesis Phase.

// ---------------- SOURCE -------------------------
#include <iostream>
#include <string>
#include <sstream>
using namespace std;

// class prototype
class Patient
{
public:
static const int MIN_LENGTH = 2;
static const int MAX_LENGTH = 40;
static const int MIN_ID = 0;
static const int MAX_ID = 9999;
static const double MIN_TEMP;
static const double MAX_TEMP;
static const double ALARM_TEMP;
static const string DEFAULT_NAME;
static const int DEFAULT_ID = 0;
static const int DEFAULT_TEMP = 98.6;

private:
string name;
int id;
double temperature;

public:
Patient();
Patient( string name, double temperature, int id );
void display();

// Accessors can be done in line:
double getTemperature() { return temperature; }
int getID() { return id; }
string getName() { return name; }

// mutators
bool setTemperature(double temperature);
bool setID(int id);
bool setName(string name);
};

// alternate way to initialize a static (required for not const ints)
const double Patient::ALARM_TEMP = 103.5;
const double Patient::MIN_TEMP = 88.;
const double Patient::MAX_TEMP = 111.;
const string Patient::DEFAULT_NAME = "nobody";

// method prototypes
string getPatientName();
int getPatientID();
double getPatientTemp();

int main()
{
// instantiate two Patients
Patient person1, person2;

// variables to capture input
string userName;
double userTemp;
int userId;

// get the info for patient #1:
cout << "Patient #1 ---\n";
// we built three helper methods for this
userName = getPatientName();
userId = getPatientID();
userTemp = getPatientTemp();

// set patient #1
if ( !person1.setName(userName) )
cout << "Error in patient name: Invalid length.\n";
if ( !person1.setID(userId) )
cout << "Error in patient id: out of range.\n";
if ( !person1.setTemperature(userTemp) )
cout << "Error in patient temperature:  out of range.\n";


// get the info for patient #2:
cout << "Patient #2 ---\n";
userName = getPatientName();
userId = getPatientID();
userTemp = getPatientTemp();

// set patient #2
if ( !person2.setName(userName) )
cout << "Error in patient name: Invalid length.\n";
if ( !person2.setID(userId) )
cout << "Error in patient id: out of range.\n";
if ( !person2.setTemperature(userTemp) )
cout << "Error in patient temperature:  out of range.\n";

// display patients
person1.display();
person2.display();

return 0;
}

// main methods (non-Patient methods)
string getPatientName()
{
string stringIn;
cout << "What's the patient's name? ";
getline(cin, stringIn);
return stringIn;
}

int getPatientID()
{
int id;
string stringIn;

cout << "What's the patient's id #? ";
getline(cin, stringIn);
istringstream(stringIn) >> id;
return id;
}

double getPatientTemp()
{
double temp;
string stringIn;

cout << "What's the patient's temperature? ";
getline(cin, stringIn);
istringstream(stringIn) >> temp;
return temp;
}

// class method definitions
Patient::Patient()
{
name = DEFAULT_NAME;
temperature = DEFAULT_TEMP;
id = DEFAULT_ID;
}

Patient::Patient( string name, double temperature, int id )
{
if ( !setName( name ) )
this->name = DEFAULT_NAME;
if ( !setTemperature( temperature ) )
this->temperature = DEFAULT_TEMP;
if ( !setID( id ) )
this->id = DEFAULT_ID;
}

void Patient::display()
{
cout << "Patient: "
<< "\n  Name: " << name
<< "\n  ID: " << id
<< "\n  Body Temperature: " << temperature << " (F)";
if (temperature > ALARM_TEMP)
cout << "\n   *** urgent: attend immediately ***";
cout << endl;
}

// mutators
bool Patient::setTemperature(double temperature)
{
if (temperature < MIN_TEMP || temperature > MAX_TEMP)
return false;
this->temperature = temperature;
return true;
}

bool Patient::setID(int id)
{
if (id < MIN_ID || id > MAX_ID)
return false;
this->id = id;
return true;
}

bool Patient::setName( string name)
{
if (name.length() < MIN_LENGTH || name.length() > MAX_LENGTH)
return false;
this->name = name;
return true;
}

/* ------------- RUN (OUTPUT) ----------------------
Patient #1 ---
What's the patient's name? bad bad
What's the patient's id #? 999123
What's the patient's temperature? 100
Error in patient id: out of range.
Patient #2 ---
What's the patient's name? hot hot
What's the patient's id #? 123
What's the patient's temperature? 104
Patient:
Name: bad bad
ID: 0
Body Temperature: 100 (F)
Patient:
Name: hot hot
ID: 123
Body Temperature: 104 (F)
*** urgent: attend immediately ***
Press any key to continue . . .
-------------------------------------------------------- */

This is a big synthesis of the assigned main() and the class.  But it is not that difficult -- perhaps an hour's worth of typos or logic errors, but it should have been fun.

We ran it twice, giving bad data intentionally to see if our program dealt with it all correctly. Chances are, in a real program, you will have found lots of little bugs that had to be fixed when doing this. 

There are some anomalies.  First, we see that patient "bad bad" got some out-of-range errors, but they didn't show up on-screen until after we entered all the data. This is just a buffer issue that I won't challenge here and now.  We would have to flush some data using special functions to get it sync'ed up right.  But it works pretty well, otherwise.

Something you could have done that I didn't do:  give better error messages.  This would be a perfect opportunity to use the public static constants (the range limits of id, name and temperature) in your main() to produce a very precise error message.  I'll leave it up to you to fix this.

7A.8.4 Finishing It Off

I am going to leave the rest to you.  At this point, you only have to do some logic in your main() to test to see which, among the two patients, should be listed first due to higher temperatures. It can all be done directly in main() without any new methods.