Section 3 - Client View of the Class
7B.3.1 Main()'s View of the MortgageData Class
We need a way to neatly encapsulate all the data of our mortgage loan into a single object. Once we do that, we have options for solving the problem of transferring that data between methods. We can pass the single object as a reference parameter to the getInput() method, or we can even return an object as a functional return.
The solution is going to be simple. But it is not going to be short. Even the simplest class, when equipped with all of the necessary accessor and mutator methods, constructors and data tends to get long, fast. But the content of the class is very repetitive, so once you see how one method works, the other methods will be clear.
Let's start with the client that we would like to be able to write, and work backwards from there.
// ordinary method prototypes void stateInstructions(); void getInput(MortgageData &loan); double computeMonthlyPayment(MortgageData loan); void reportResults(double result); int main() { double answer; MortgageData loan; stateInstructions(); getInput(loan); answer = computeMonthlyPayment(loan); reportResults(answer); }
Notice two things about this main():
- There are no global variables. The data is now completely described and passed as objects which are local to main().
- There is a new data type called MortgageData. Without seeing the details of this type, we fully understand its purpose. It holds the information that is returned by getInput() (not through the functional return, but by the reference parameter) and is used to transport that information to computeMonthlyPayment().
You have to appreciate the beauty and simplicity of this main() and what it represents. We often write such client methods before defining the data that it uses. This will help us understand the class MortgageData from the "class user" point of view, which later leads to the proper definition of the class from the "class designer" perspective.
7B.3.2 getInput()'s View of the MortgageData Class
Main() is not the only place, of course, that the MortgageData objects will be used. As you see, getInput() takes a MortgageData reference, so it must be filling that object with useful data during its definition. Let's examine a small portion of the new version of getInput(), looking at the MortgageData class from a class user (not class designer) perspective:
// gets input and leaves the values in its reference parameter void getInput(MortgageData &userData) { double dblResponse; cout.setf(ios::fixed); cout.precision(2); // get principal do { cout << "\nEnter amount of the loan. Only use numbers, \n" "please. No commas or characters like '$'.\n" "Amount must be between " << MortgageData::MIN_LOAN << " and " << MortgageData::MAX_LOAN << ".\n" "Your loan amount: "; cin >> dblResponse; } while (!userData.setPrincipal(dblResponse)); // get interest ... similar // get length of loan ... similar }
First look at the method header (aka. the "method signature"):
void getInput(MortgageData &userData)
The fact that there is a MortgageData& parameter tells us that this method may very well modify the MortgageData reference passed in.
As you can imagine, this MortgageData classwill have not only a principal member, but also interest rate and loan term members. These three doubles will be the primary data of our class. Without seeing the definition at this point, we can still anticipate how the class is to be used. Since setting class data with an accessor (mutator) method should filter out bad data, it is common for such a method to return a bool: true if the assignment was successful and false if the value was out-of-range. We are going to make our methods behave exactly this way, and as you see, we can use the bool return value to help us control an input do/while loop. Study the above while phrase until you fully understand why it works.
Next, we have to explain the MIN_LOAN and MAX_LOAN members. We use our sleuthing skills and knowledge of classes to figure out what these are. If a class member is accessible from outside the class then it is public. So this is a public member. Also, if a member is dereferenced through an object it is an instance variable, while if it is dereferenced through the class name it is a static class variable. We are dereferencing it like so: MortgageData::MAX_LOAN, which is to say, through the class name. Therefore, MIN_LOAN and MAX_LOAN are public static members. Their name tells us what they mean. These are two doubles that determine the range of values our class will accept for its principal loan amount.
This is another example of why we sometimes declare members to be static class members. These min and max values are obviously the same for every object in the class.
7B.3.3 computeMonthlyPayment()'s View of the MortgageData Class
We continue to look at how our client is going to use this new class. computeMonthlyPayment() takes an object of this class as a formal parameter. It uses that object to extract the three primitive doubles that it needs for the computation. Here is a code fragment that shows this, focusing on the getYears() accessor method of the MortgageData class:
// computes and returns answer double computeMonthlyPayment(MortgageData loan) { // local variables needed only in this method double dblTemp, dblPmt, dblMonths, dblMoRt; // convert years to months dblMonths = loan.getYears() * 12; // etc ..., then return dblPmt; }
This gives us clues about how we should write our MortgageData class.