Previous Section Main Menu Next Section

Chapter 4

Design with Functions

Section III: Output, Calculation, and Display Functions Section I: Decomposition Section II: Functions Section IV: Parameters
Section V: Tracing Section VI: Final Thoughts on Redesigning the Employee Salary Problem

A. Output Functions
It's now time to work on some of the other functions. If you have followed the discussion in the previous section, you should find it easy to code "GetHoursWorked" so let's try OutputSalary next. It turns out that 'OutputSalary' is almost the opposite of 'GetRateOfPay' and demonstrates another simple but important design pattern - the "Basic Output" pattern. The design starts as follows:

OutputSalary
Purpose: To output the salary of an employee
Goal: The screen displays salary

The only new idea here is that a 'goal' can include how the screen looks. Notice that there is no internal state change. When doing an output to a monitor, the system retrieves the value(s) found in one or more memory locations and displays those values on the screen. However, the contents of those memory locations are not changed.

We move now to asking our two questions. First:

If someone asks you to go to the board and write down the value they are thinking of, what information do you need from them? What you need, of course, is the value they are thinking of. The same with our output function. It needs whatever is to be output - the salary in this case. Now, for question number two:

When you are finished writing the value on the board (using the same example), is there some information you return to whomever made the request - other than to say you are done? No! Similarly, with this and most output functions - nothing is returned. The design then becomes:

OutputSalary
Purpose: To output the salary of an employee
Goal: The screen displays salary

Receives: <salary>, a double
Returns NONE

And finally, the algorithm:

Algorithm:

Output the <salary>

Most functions whose purpose is to output a value have the same design. This then is an example of our second design pattern, the "Basic Output" pattern:

The Basic Output Pattern

	Purpose: 	To output some value(s)
	Goal: 		The screen displays the value(s)
Receives: The value(s) Returns: NONE
Algorithm: Output the value(s)
***************

As before we need to declare and define this function. First the declaration:

void OutputSalary (double salary);

This time we put the word 'void' in front of the function name because the function returns nothing. Inside the parentheses we place the type and name of the one variable that is to be received.

void OutputSalary (double salary);

As with all parameters, this is a local variable, known only inside this function. We could have chosen any name we wanted, including the variable name 'fullSalary' used in 'main'.

Also, as before, we need to define this function but this is easy given our algorithm.

Here the code is even simpler than in 'GetRateOfPay' - one line! There is no 'return' instruction because there is nothing to return. Such one line functions are not uncommon, but why would we want them? As noted above, they allow us to modularize our code. Later, we can make this output as complex as we want or we can even experiment with different forms of output without changing any other part of the code. Even experienced programmers are often tempted to not use functions when they first start writing a program. They know, however, that a section of code that looks simple at the moment can easily become complex as the requirements and understanding of the program grow. In the long run, modularity through the use of functions makes the programmer's job easier and helps avoid errors. Trust us for now if you are not already converted!

Before reading on, see if you can integrate this code into the original program. When you are finished, compare what you have done with the code in the file ch4prg1.cpp.

Points to stress about this code:


B. Functions That Calculate a Value
We move on to the function that calculates the employee's salary and again start with the design. As usual, if the task decomposition process has been done correctly, it is easy to state the purpose and goal of this function.

CalculateSalary
Purpose: To calculate an employee's salary
Goal: <employeeSalary>, a double, contains the employee's salary

To determine the receives and returns we do what we have done in the previous two cases and ask our questions.

1. What, if any, data does the called function need from the calling function to accomplish its task?

2. What, if any, data does the calling function expect back?

Anytime a function has as its purpose some kind of a calculation one can assume that it needs to return the result of that calculation. One should then check to see if the function needs to receive the values involved in the calculation - perhaps not including constants. We therefore have the basis for another design pattern - Pattern 'C' of the Function Design Patterns .

Since 'CalculateSalary' has no other way of knowing the rate of pay and the hours worked, it does have two receives - the rate of pay and the hours worked. The value calculated and returned is the salary. Here then is the result of the design phase including an algorithm that reflects the code we wrote in chapter two.

It is now time for the function declaration:

double CalculateSalary(double rate, double hours);

Note that we provide a name and type for each value to be received but only a type for the value to be returned. The calling function will use the returned value however it wishes, so it and not the called function has the responsibility of providing a variable name. On the other hand, the received values are used in the function's code - as 'rate' and 'hours' will be used in this function's code.

The only new idea here is that there are now two local variables inside the parentheses, reflecting the fact that two items are received by the function. Note that there is a comma separating the two items, which, by the way, are technically called parameters.

The function definition is simply a translation of the algorithm. For us it is even easier than that because we have already written the code in chapter two - see the 'if' statement in the function 'main above. We only need to change 'hoursWorked' to 'hours' and 'rateOfPay' to 'rate' to reflect the names of our local variables. Thus, the definition looks as follows:

double CalculateSalary(double rate, double hours)
{	double regularSalary;
	double overtimeSalary;
	double fullSalary;
	double overtimeRateOfPay;
		
	if (hours > REGULAR_HOURS)
	{	regularSalary = REGULAR_HOURS * rate;
		overtimeRateOfPay = rate * OVERTIME_ADJUSTMENT;
		overtimeSalary = (hours - REGULAR_HOURS) *   overtimeRateOfPay; 
		fullSalary = regularSalary + overtimeSalary;
	}
	else
	{ 	fullSalary = hours * rate;
	}
	return fullSalary;
}

Putting this function's declaration and definition in their proper places in the full program are left as an exercise for you. We do, however, need to discuss the code that will call this function.

In the previous versions of this program, 'main' included the code for the algorithm we just discussed. Now we witness a drastic reduction and simplification of the 'for' loop found in 'main.':

We have already discussed the function call "rateOfPay = GetRateOfPay();". Although we have not taken the time to design and code the function 'GetHoursWorked' here, we have already noted that it is similar to 'GetRateOfPay'. Not only is it similar in design and implementation, the call is essentially the same.

The function call

fullSalary = CalculateSalary(rateOfPay, hoursWorked);

is like all the others except that there are two parameters (two values to be passed to the function definition) and the parameters are separated by a comma. Two parameters are needed of course, because 'CalculateSalary' has been declared as receiving two values.

Notice how easy the loop' is to comprehend. We are looking at something like an executive summary. The key steps are all there but the details have been removed. Again, this is an important part of complexity reduction through functional decomposition.

C. The Display Functions
The only two functions left to discuss are the two display functions. They are both pretty much the same so let's focus on 'DisplayIntroScreen'. A quick analysis indicates that its sole purpose is to output a message. Since it knows its message, it does not need to receive anything. As with most output functions (see the discussion of 'OutputSalary'), it also does not need to return anything and has a very simple algorithm. Here then is the design:

DisplayIntroScreen
Purpose: To output a introductory message
Goal: The introductory message is displayed

Receives: NONE
Returns: NONE

Algorithm

Display the introductory message.

The function declaration looks as follows:

void DisplayIntroScreen();

The code to be included in the definition is made up of some number of 'cout' lines to output the desired introductory message.

That concludes our work on this modularized version of this program. A final version of this code is available but you are urged to attempt it yourself before reviewing that code. As part of that effort you might want to review the basic function design patterns introduced in this chapter.

Top of Section Main Menu Next Section