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.
- Ask for patient data.
- Each time we get data from the user, echo it to see if we have read it in correctly.
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.
- Instantiate both patients.
- Get info for patient #1.
- Use mutators to set the data for #1.
- Do it all now for patient #2.
- Print out both patients.
// ---------------- 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.