cs1ch6sec2.htm
 
CHAPTER 6

Giving Olympia A Better Interface
or
User Interface Design - Some Preliminary Techniques and Implications


Section II: Functional Decomposition of 'main' Section I: User Interface Analysis - Giving Olympia Choices Section III: C++ Code Section IV : Putting It All Together


  


Table of Contents

Learning C++:
An Index of Entry Points


2. The

of C++

A reference document on the basic elements of C++.



3. The Patterns





A. Discovering a Set of Functions
Back in chapter four, we explored how to decompose a problem in order to modularize it and simplify the design of 'main'. We need to use that methodology again here because the issues we are focusing on have little to do with the 'Contract' class and much to do with the program that uses that class. (With systems involving truly complex user interfaces, the interface itself may become a class with one or more instances.) The question then is, what functions might this program need that do not belong to a specific class.

The Interface analysis does help us here. The tasks referred to there included:

  • getting a choice of action from the user
  • getting a contract number from the user
  • getting a property to be changed
  • getting the new value of the property to be changed
    - this will really be three functions: one for each of the properties in Contract
  • displaying the property values of a class
  • displaying an opening message
  • displaying a closing message
Implicit in all this is the need for a function to perform the actual changes to the property values. Therefore, we probably need the following functions:
GetOperatorChoice
GetContractNum
GetProperty
GetSquareFootage
GetNumDesks
GetNumDays
DisplayInfo
ChangeInfo
DisplayIntro
DisplayClosingMessage

We do not yet know whether users will be asked all of the questions in 'main' or elsewhere. Should we have 'main' request both which contract to work with and which operation to perform on that contract, or should we only have 'main' ask which operation the user wants to perform and have the functions 'DisplayInfo' or 'ChangeInfo' ask which contract? Let's explore the first way in this example.

Now, do any of these functions need further decomposition? The 'intro' and 'closing' functions as well as the 'Get...' functions are probably simple enough - we have already worked with these kinds of functions and found them to not be difficult. How about the other functions? The function 'DisplayInfo' will send messages to the instance of contract under consideration seeking the information it needs to display. In this sense it uses or calls other functions. However, these are functions already designed and implemented as part of the class design we did in chapter 5 so we have no further design work to do.

'ChangeInfo' will likewise use member functions of the class 'contract' but it has some added tasks. First, it needs to find out which property(ies) the user wants to change. Therefore, we need a function to ask the user which property(ies) he or she wishes to change. Of course, we already have such a function. What we are discovering here is that 'GetProperty' is used by 'ChangeInfo'.

'ChangeInfo' also needs to ask the user for the new value of the property being changed before it calls the member function to make the change. Again, we have such functions in 'GetSquareFootage', 'GetNumDesks, and GetNumDays' . Our conclusion is that these too are subtasks of 'ChangeInfo'.

Here is a structure chart of this program as designed. There are two points to note: First, the member functions used in the program are not included in the chart. Second, since structure charts are a general tool, not designed specifically for C++ or any programming language, we use a more English like naming scheme for the modules.


B. Functional Design
Having determined a likely set of functions, it is time to work on the Purpose/Goal, Receive, and Return analysis for each function. Here we shall look briefly at two functions, 'DisplayInfo', and 'ChangeInfo'. The purpose of these is straightforward - to display the values of the properties of a contract or change the value(s) of one or more properties of a contract.

Now, what do these functions need from the function(s) that call them and what will they return? Consider 'DisplayInfo' first. From our earlier discussions of functions that output values, we know that this function needs to receive the values to be output. In the case of 'DisplayInfo' there are four such values - the square footage, the number of desks, the number of days, and the per week charge. We could then say that this function has four receives but another way to answer this is to say that the function simply needs a contract since each contract 'contains' these four pieces of information. The advantage of this new approach is that it allows new properties to be added to the contract design or old properties removed without changes to the parameter list. If eventually contracts acquire more properties, the values for those new properties will still be provided to 'DisplayInfo' as long as the contract itself is provided.

In terms of 'returns', we know that functions that output values usually do not return anything and 'DisplayInfo' is no exception. Here then is the design for this function.

DisplayInfo
Purpose: To display the values of the properties of a contract
Goal: The monitor contains the values of the properties of a contract

Receives: A contract
Returns: NONE

The 'receives' for 'ChangeContract' are the same. This function cannot change a contract unless it has one to change. However, the situation is a bit more complex in the case of 'returns'. Whichever function calls this function would, presumably, like to get back the changed contract. As a general rule, whenever a function changes values, it probably should return what got changed. So, 'ChangeContract' both receives and returns a contract. The design looks as follows:

ChangeInfo
Purpose: To change the values of one or more of the properties of a contract
Goal: The values of one or more of the properties of a contract have been changed

Receives: A contract
Returns: A contract

C. An Improved Decomposition
It turns out that there is one further modification we could make to our general design. As noted, the program we are working on should allow a user to continually select a choice of operations until he or she is finished. This is a very common, basic program requirement - one that is often handled by a separate function. In Chapter 4 we briefly discussed the idea of keeping 'main' as simple as possible. Given this, let's shift all the functionality we have discovered above, except for that of displaying the introductory and closing messages, to a new function called 'ProcessUserChoices'. This, in turn, modifies the structure chart:


The purpose of 'main' is now very simple:

    Purpose: To Display the introductory and closing messages and, in between, call the function that controls the program.

The algorithm then looks as follows:

    DisplayIntro
    ProcessUserChoices
    DisplayClosingMessage

D. Top Down or Bottom Up Design
There are a number of ways we could proceed. We could focus our attention on some of the simpler functions at the 'bottom' of the structure chart and work our way up to the top, designing as we go. This is called bottom up design. Or, we could start at the top and work down - top down design. Most programmers prefer one of these two approaches but are not rigid about it and jump around as needed in what has been called, for the fun of it - middle out design. Let's use the top down approach here but jump around when it seems best to do so.

Our task then is to design the algorithm for' ProcessUserChoices' - the first function not yet designed starting from the top of the structure chart. It's purpose, as we started to describe above, is to:

    Purpose: To continually process user requests to change or display contract information until the user indicates he or she is done.

    Goal: The user's requests have all been handled

As for receives and returns, ' ProcessUserChoices' does not need anything from 'main' nor does it return anything to 'main'. This is clear when you look at the algorithm for 'main'. It simply handles the introductory and closing screens and passes off the real work to ' ProcessUserChoices'.

E. Boolean Variables and Another Pattern
We now move onto designing the algorithm for 'ProcessUserChoices'. You may recall from chapter 3 that the purpose statement can be used to determine if any patterns can be used to assist in the design of a function's algorithm. The purpose statement here certainly implies a loop and a quick review of the loop patterns turns up a pattern that continues until a user enters a sentinel value - the "While Looping With a Priming Read" pattern. This sounds like what we need so let's explore its usage here - using 'Q' as the sentinel value the user is to type in when finished..

    	Get choice
    	While choice != 'Q'
    	{	GetContractNumber
    		if choice = 'D' then
    		{	DisplayInfo(contract number)
    		}
    		else
    		{	if choice = 'C' then
    			{	ChangeInfo(contract number)
                      	}
    		}
    		Get choice
    	}
    
It seems to work. 'Let's refine the problem a bit and see how well this algorithm stands up. In our interface analysis we stated that the user would be asked if he or she really wanted to quit. As it stands, this algorithm stops without any such question. How can we fix this? Since it is possible that the loop should continue after the user has typed 'Q' - because the user can signal, "No, I did not mean to quit." - we have to change the 'while test'. There are a number of ways to deal with this but the easiest way is to add what we will call a boolean variable. Such variables have only two values, TRUE and FALSE. (One can also think of them as YES and No.) In our case we could declare a boolean variable called 'done' and have the 'while' loop execute until the user is sure he or she is done. The algorithm would look like this:
    done = FALSE
    While not done
    {	Get choice
    	GetContractNumber
    	if choice = 'D' then
    	{	DisplayInfo(contract number)
    	}
    	else
    	{	if choice = 'C' then
    		{	ChangeInfo(contract number)
    		}
             	else
    		{	if choice = 'Q' then
    			{	output "Are you sure you are done, Y or N
    				read answer
    				if answer = 'Y' then
    				{	done = TRUE
    				}
    			}
    		}
    	}
    }
    
First, we set 'done' to FALSE. This guarantees that we will enter the loop. Be sure you understand why:

    A 'while' loop executes as long as the condition being tested is TRUE. The test 'not done' is TRUE when 'done' is FALSE because 'not FALSE' is TRUE. When 'done' is finally set to TRUE, the test condition will become FALSE and the loop will stop.

Notice where 'done' is set to TRUE. Inside the loop we have a new option - when 'Q' has been entered for 'choice'. When this happens, we ask the user if he or she is sure they want to exit. If they say 'yes', 'done' is set to TRUE and the loop will stop executing. If the person says 'no' (or anything else for that matter), 'done' is not changed, that is, nothing happens, and the loop continues.

So far so good, but there is a problem. Before proceeding, see if you can find it, first by simply reviewing the algorithm and then, if the problem is not obvious, by tracing it.

What do you think the problem is? If you said that users are forced to enter a contract number even if they have entered 'Q' to signal that they are finished, you are correct. If you said something else, be sure you see the problem now- perhaps by tracing the algorithm again.

To fix this we need to move the call to Get Contract Number inside the two 'if' statement branches that need a contract number - when the person types a 'D' or a 'C'.

    done = FALSE
    While not done
    {	Get choice
    	if choice = 'D' then
    	{	GetContractNumber
    		DisplayInfo(contract number)
    	}
    	else
    	{	if choice = 'C' then
    		{	GetContractNumber
    			ChangeInfo(contract number)
    		}
    		else
    		{	if choice = 'Q' then
    			{	output "Are you sure you are done, Y or N
    				read answer
    				if answer = 'Y' then
    				{	done = TRUE
    				}
    			}
    		}
    	}
    }
    
Notice that since the 'while' test no longer involves a value input by the user, we do not need a priming read and the 'Get Choice' instruction is moved inside the loop and removed from the bottom of the loop. We are no longer using the priming read pattern. Instead, we have a new pattern. In appendix B this pattern has the name "Loop Until User Confirms Choice to Quit". As this pattern is implemented in appendix B is uses a new instruction discussed in Part B of the next section.

Top of Section Main Menu Next Section