Previous Section Main Menu Next Chapter

CHAPTER 8

AN EVEN BETTER CONTRACT PROGRAM


Section VIII: Rewriting the Non-member Functions Section I: Analyzing the Code Implications of an Improved Contract Program Section II:Instances as Array Elements Section III: The List: A Look at Data Representation
Section IV: Lists: Working with Encapsulation Section V: Class Declarations for Contract and ListOfContracts Section VI: Member Functions for ListOfContracts Section VII: Improving ListOfContracts

A. The Overall Design
We have new user options to add and a whole new class to work with. It's time to integrate all this into the program code in the file containing 'main'. In chapter six we developed a structure chart for a simpler version of this program. The first part of this chart remains the same, with 'main' calling 'ProcessUserChoices', 'DisplayIntro' and 'DisplayClosingMessage'. However, 'ProcessUserChoices' has gotten more complex.

That is because here and there in this chapter we have added functionality to the program. In Section I above we talked about adding the ability to add and delete contracts, while in Section II we discussed the need for the 'InitializeInstances' and 'DisplayAll' functions. We also now have to get contract ID's from the user. There are enough new or modified functions that a revised structure chart would be useful.

What makes 'ProcessUserChoices' complex is that it handles all the processing for each option. Therefore, one way to simplify this function would be to create functions that handle all the processing for each user option and then have 'ProcessUserChoices' simply call these functions. Only one function call is removed from 'ProcessUserChoices', the call to 'GetContractID', but all the inner loops are removed.

As the structure chart in Figure 8.7 shows, 'DisplayIntro' and 'DisplayClosingMessage' still do not have any function calls in them - they do not have any boxes hanging off them. 'ProcessUserChoices' is still at the heart of the program and it does make a number of function calls.

By adding more functions deeper in the structure chart, 'ProcessUserChoices' is easier to follow as shown in the code below:
void ProcessUserChoices()
{

// Code to create the list of contracts
ListOfContracts contracts;
InitializeInstances(contracts);
bool done = false;   // Is the user finished processing contracts?
char choice;
while (!done)
	{	choice = GetOperatorChoice();
                switch (choice)
		{	case 'D':
			case 'd':
				DisplayContract(contracts);
				break; // case 'd'
			case 'C':
			case 'c':
				ChangeContract(contracts);
				break; // case 'c'

			case 'L':
			case 'l':
				DisplayAllContracts(contracts);
				break;

			case 'A':
			case 'a':
				AddContract(contracts);
				break;

			case 'E':
			case 'e':
				DeleteContract(contracts);
				break;

			case 'Q':
			case 'q':
				cout << "Do you want to really want top quit (Y) or (N)\n";
				char answ;
				cin >> answ;
				if ((answ =='Y') || (answ == 'y'))
				{
					done = true;
				}

				break;
			default:
				cout << "Invalid choice. Please try again";
				choice = GetOperatorChoice();
		}  // end switch
	}
}
Clearly the code is much easier to read. Of course, we need to declare and define the new functions 'DisplayContract', 'ChangeContract' 'AddContract', 'DeleteContract', and 'DisplayAllContracts'. We trade cleaner code for a bit of extra work.

A few points to clarify:

  1. 'DisplayContract' and 'ChangeContract' are not new names for 'DisplayInfo' and 'ChangeInfo'. Rather, they are the functions that handle all the processing for the two choices and, in the process of doing so, call 'DisplayInfo' or 'ChangeInfo'. For example, 'DisplayContract' will keep asking which contract to display and call 'DisplayInfo'.

  2. Don't confuse the new non-member functions 'AddContract' and 'DeleteContract' with 'Add' and 'Delete', the member functions belonging to the 'List' class. 'AddContract' does the work to create a contract and then calls 'Add' to insert the contract onto the list. 'DeleteContract' keeps asking for contracts to be deleted and calls 'Delete' to modify the list. Likewise, 'DisplayAllContracts' is not to be confused with the member function, 'DisplayAll'.
Now we can work on the function declarations. Since the five functions that handle the user's choices all work somehow with the list of contracts, they are all passed the list. In each case, the list is passed by reference. In the case of 'ChangeContract, 'AddContract', and "DeleteContract' this is necessary because we expect these functions to return a changed list. For the other two functions the reason involves writing efficient code. As we have noted, when passing something by value, one is making a copy of the data being passed. A list of contracts represents a potentially huge amount of data that would all have to be copied if the list were passed by value. Passing the list by reference avoids this copy process and speeds up the code. (By the way, this is one reason why arrays themselves are always automatically passed by reference.)

Writing the declarations should now be simple so let's examine the code for these functions. The code for 'DisplayContract' and "ChangeContract' borrows from the code that used to be in the switch statements but has to be changed to reflect the fact that we are now using a list of contracts. As before, the user is asked for a contract ID (via a call to 'GetContractID'), and the code then loops until the user enters a 0 as the ID. (Remember, this allows the user to continue to display or change contracts without having to repeatedly enter a 'D' for 'Display or 'C' for Change.)

Inside the loops for both these functions the code is changed because the main program no longer has direct access to the array of contracts. We start with 'DisplayContract'. It must first use the list member function 'FindContract' to see if a contract with the entered ID exists on the list. If so, 'FindContract' returns both TRUE and the contract itself. This contract is then passed to the function 'DisplayInfo' that we developed in Chapter 6, Section III. . If no contract with this ID is found, 'FindContract' returns FALSE and an error message is output. Here is the code:

void DisplayContract(ListOfContracts& contracts)
{ int contractID;
  Boolean found;
  Contract contract;
  contractID = GetContractID();
  while (contractID != 0)
  {  found = contracts.FindContract(contractID, contract);
	  if (found)
	  {  clrscr();
		  DisplayInfo(contract);
	  }
	  else
	  {	cout << "\nContract " << contractID << " is not in list.\n";
	  }
	  contractID = GetContractID();
	}
}
The function 'ChangeContract' is almost the same except that inside the loop the function 'ChangeInfo' is called followed by the List member function 'ReplaceContract'. (Remember that the modified contract needs to be replaced on the list.) Again, here is the code:
void ChangeContract(ListOfContracts& contracts)
{  	int contractID;
	Boolean found;
	Contract contract;
	contractID = GetContractID();
	while (contractID != 0)
	{  found = contracts.FindContract(contractID, contract);
		if (found)
		{
			ChangeInfo(contract);
			contracts.ReplaceContract(contract);
		}
		else
		{	cout << "\nContract " << contractID << " is not in list.\n";
		}
		contractID = GetContractID();
	}
}
The function 'DisplayAllContracts' is simple – it just calls the list member function 'DisplayAll' to handle all the work.
void DisplayAllContracts(ListOfContracts& contracts)
{ 	contracts.DisplayAll();
}
We now look at 'AddContract' and 'DeleteContract'. Like 'DisplayContract' and 'ChangeContract', these two functions begin by asking for a contract ID and then loop until the user enters a 0. In the case of 'AddContract', the code inside the loop consists of first, a call to a function, 'CreateContact', that fills a new contract with information supplied by the user and second, a call to the list member function 'Add' (whose code and description is found here). "DeleteContract' is even simpler in that it only requires a call to the list member function 'Delete' (whose code and description is found here). Here is the code:
void AddContract(ListOfContracts contracts)
{  	int contractID;
	contractID = GetContractID();
	Contract contract;
	while (contractID != 0)   // The user wants to add another contract
	{	CreateContract(contractID, contract);
		contracts.Add(contract);
		contractID = GetContractID();
	}
}

void DeleteContract(ListOfContracts& contracts)
{  	int contractID;
	contractID = GetContractID();
	while (contractID != 0) // The user wants to delete a contract
	{	contracts.Delete(contractID);
		contractID = GetContractID();
	}
}
We close our discussion of code with a look at 'InitializeInstances'. In Section II we observed that this function could take any number of forms, depending on how we want to carry out the task of creating the initial set of instances. The version we use here remains essentially the same as the one in Section II, except that we need to call the list member function 'Add'. This is necessary because, as we already noted, the main program does not have direct access to the array of contracts. The complete code for this is in the file 'ch8tst5.cpp'. Here we will look at how the first two contracts are initialized onto the list.
Contract contract;

	contract.ChangeID(1111);
	contract.ChangeSquareFootage(1000);
	contract.ChangeNumDesks(3);
	contract.ChangeNumDays(5);

	contracts.Add(contract);

	contract.ChangeID(2222);
	contract.ChangeSquareFootage(500);
	contract.ChangeNumDesks(10);
	contract.ChangeNumDays(3);

	contracts.Add(contract);
Note how only one contract is instantiated. To add the first contract to the list, this one contract is filled with the correct data and 'Add' is called. Then, to add the second contract to the list, the same contract is filled with the correct data for contract two and 'Add' is called again. Since 'Add' copies whatever it is given onto to the list, the single contract in this code is nothing but a placeholder for whatever information is being entered by the user. This process can be repeated until all contracts have been added to the list.

B. Summary
We have now designed and implemented a program with real potential. It allows users to view, modify, add, and delete contracts. We also have a general pattern - the list pattern - to handle a whole range of problems. Whether one wants to keep track of houses, swimming pools, people or whatever, the general framework for lists described here could be used. In fact, if the class types were changed to represent 'houses' or swimming pools or whatever, the main program would require little change. Try and remember that much of programming involves reusing what you or someone else has already designed, implemented, and tested. Saving work and avoiding bugs is part of what it is all about.

Of course, the final step is to compile, link, run, and test this code. Follow the procedure you used in chapters 5 and 6 to get your program running. The only trick is that now there are three .cpp files to link. (Borland C++ users can click here to get further instructions. You can find all the code in the files, 'common.h', 'contrct2.h', 'contrct2.cpp', 'ch8list2.h', 'ch8list2.cpp', and 'ch8tst5.cpp'.

This has been a long chapter. Click here if you would like to read a more detailed summary

Top of Section Main Menu Next Chapter