cs1ch5sec4.htm
 
CHAPTER 5

Object-Oriented Design
 
 
Section IV: Classes in C++ Section I: The Object-Oriented Paradigm Section II: Class Analysis and Design Section III : CRC Cards
Section V: Defining Classes Section VI: Using Class Declarations in a Program Section VII: Constructors Section VIII: Expanding the Idea of Classes


  


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





This ends the analysis phase and we proceed to design. In chapter three we saw that design involved determining the purpose, receives, returns, and algorithm for each function. We will use much the same approach here. Each behavioral responsibility of a class is represented by a member function of the class. Our first task then is to design these functions, that is, determine the purpose of each function, what each one receives and returns, and, if necessary, write an algorithm for the function.

There is an additional part to class design. At this point we have listed the knowledge responsibilities of each class. In our case, we have also decided which knowledge responsibilities will be implemented as properties. (See the discussion of 'Provide Per Week Charge' in the previous section.) Under other circumstances but we would not have decided this yet and would have to do so now. In either case, we still need to decide how to implement the properties. When implemented in a class declaration, properties are often referred to as data members since they are data belonging to instances of a class. We will start using the terms property and data member interchangeably. We will see that designing data members can be quite complex. For now, however, it is simply a matter of determining the type (for example, int, double, or char) of each property/data member.

In complex programming problems the design stage usually is kept separate from any coding. Here, we will blur the distinction a bit by discussing class design in the context of declaring a class.

A. Classes as Types
This ends the analysis and design phases. It is now time to begin coding. Before we do so, however, let's clarify the meaning of and the relationships among the words type, class, object, and instance. From the earlier chapters we know what a type is in C++. A type is a name for some category or classification. Integer is a type; 3 is an instance of the type integer. We have used this idea of type when we declared a variable to be of some type, for example:

double thisNumber;

Classes can be seen as complex types - they certainly name some category or classification. A class then is nothing but an idea, a concept, a description. In a program a class only represents the possibility of a certain type of object. It is the instances of a class that represent actual objects. Thus, only instances of a class can be used/manipulated in a program.

The process is that a programmer first declares a class (describes the properties and behaviors of the class). He or she then defines the behaviors of the class (remember the difference between declaring and defining a function). And finally, he or she declares instances of the class.

The interesting thing about a class (or type) is that one can imagine a class that does not have any actually existing members. Most of us can imagine the class "hairy monsters with three heads that live on the far side of the moon". It is doubtful, however, if instances of such a class exist.

B. Class Declarations and Encapsulation
Up to now we have only worked with the built-in C++ types, for example, int, double, and char. We have also worked with many of the built-in operators (the arithmetic and relational operators) and statements such as 'while' and 'if'. If we were limited in C++ to using the built-in functions and types, the language would be powerful but awkward to use. Using function declarations and definitions, however, we have moved one step farther in terms of statements and have actually created our own additional statements because a function call can be considered a statement - it tells the system to do something. What it tells the system to do, of course, is the set of instructions in the function definition.

Therefore, when we write functions we are extending the instruction set of the language! We need to be able to do the same with types. In the last section we introduced the idea that a 'class' is a type. In this section we explore this idea further and study one way to extend the set of types built into C++ - by declaring classes. To make our discussion concrete we will continue with the class 'contract'.

To declare a new class one starts with the word 'class' followed by the class's name and a set of curly brackets to indicate that all that follows is related to the named class. Oh, and don't forget the semicolon at the end - when forgotten, some strange compiler errors can result.

    class Contract
    {
    .
    .
    .
    };

Back when the object oriented paradigm was first introduced we talked about the idea of encapsulation - that the data members (properties) of a class are usually not directly accessible to the rest of the program. We hinted that it would be the member functions that would provide the access. We also stated that the member functions of a class would implement the behaviors of the class, the behaviors that would be used by other parts of the program. These functions must, therefore be accessible outside the class and its instances- or how then could they be used.

The first thing to understand then is that classes have public and private parts. Those elements of a class that are to be encapsulated in the class are declared in the private part. It is this 'private' declaration that causes the data members - and their organization or structure - to be encapsulated. (Our simple data members here do not have any organization or structure to speak of. Later, however, in Chapter 8, Section IV we will see examples of structure.)

In almost all cases the data members are private to a class. Access is only through a set of member functions (the interface) whose specific tasks are to provide or change the values of the data members of a class. To make these functions accessible they must be included (declared) in the public part of a class. In other words, any member function that represents a responsibility of the class/object or has the sole purpose of allowing access from outside the object to the member data of an object must be declared in the public part of the class. As noted earlier, such functions are called interface functions and you should make sure you include all necessary interface functions in your class design.

All this means that class declarations are complex and one might want to ask again why have encapsulation! After all, shouldn't I as a programmer be able to change the value of any variable/data member that I want to from anywhere in a program ? From one perspective the answer to this question might well be 'yes' - if programmers were perfect and never made mistakes. Unfortunately, they aren't and encapsulation helps avoid mistakes. It also both allows programs to be more easily modified and supports team programming. A programmer can quickly devise a class, complete with the necessary interface functions and give it to others to use. The programmer can then go back and improve on the class while the other team members write code based on the class interface. As long as the programmer's improvements do not change the interface, the modifications have NO effect on the code written by those using the class. If the other team members had direct access to the insides of the class and took advantage of the internal structure, any changes made to that structure would require changes in other team member's code.

This is the same way that a car works. One can have the engine, the carburetor, the exhaust system etc. changed and still not need to re-learn how to drive since the interface (steering wheel, pedals ...) are all the same.

Back to the design: member functions also can be private. Clearly the interface functions must be public since they must be accessible to any code that wants to manipulate an instance/object of a class. Private functions are those functions that are known only by the objects of a class and therefore they cannot be used outside the objects. Usually such functions exist because the programmer decides that some member function is too complex or has elements that can be re-used in other member functions. He or she then decomposes the member function as in chapter four. It is these sub-functions that are inaccessible outside the object and therefore are private.

******************

Since classes have public and private parts, the code for our 'contract' class must reflect this:

    class Contract
    {public:
    .
    .
    private:
    .
    .
    };

The words 'public' and 'private' could be interchanged but most experienced programmers prefer the order we have chosen. This encourages the focus to be placed on the public part of the class as opposed to the private part. Since the public part of the class comes first and the member functions are public, let's first focus on designing and declaring them. Here is where we really use the material from the earlier chapters so make sure you are comfortable with function design, declaration and definition.

The member functions that we have determined need to be included in the class 'contract' are:

    Change Square Footage
    Change Number of Desks
    Change Number of Days
    Provide Square Footage
    Provide Number of Desks
    Provide Number of Days
    Provide (Calculate) Per Week Charge

C. Declaring Functions that Change the Value of a Property
There are three functions whose task it is to change the value of one of the properties of an instance. The design for them is all essentially the same so we will carefully walk through the design of only one, 'Change Square Footage'. As so often is the case, the purpose is obvious from the name, as is the goal state. All that is left is to decide what is received and returned. So, what does this function need in order to accomplish its task? And, the answer is (trumpet roll): the new 'square footage' value. Finally, does this function need to return anything to the function that called it? No, for the same reason that output functions do not return values usually. Here then is our design:

ChangeSquareFootage
Purpose: To change the value of the square footage data member
Goal: The square footage data member has a new value

Receives The new Square Footage (an integer)
Returns: NONE

The design for the other two 'change property' functions are exactly the same and here we have another pattern. All member functions whose purpose is to change the value of some property of a class have the same design. They also have the same declaration form as is shown below:

    class Contract
    	{public:
    	   	   
    	   void ChangeSquareFootage(int sqFootage);
    		/*
    		Purpose:	To change the value of the square footage data member
    		Goal:		The square footage data member has a new value
    
    		Receives	The new Square Footage (an integer)
    		Returns:	NONE
    		*/
    
    	void ChangeNumDesks( int numDesks);
    	// Design comments go here.
    
    
    	void ChangeNumDays( int numDays);
    	// Design comments go here.
    	.
    	.
    	private:
    	.
    	.
    	};
    

D. Declaring Functions that Return or Provide the Value of a Property
There are four functions that provide the value of a property belonging to some instance of the class ' contract'. As discussed earlier, three of these ('ProvideSquareFootage', 'ProvideNumberOfDesks', and 'ProvideNumberOfDays') simply return the value found in the relevant property. As with the 'Change....' functions the design and declarations of these are all the same. A quick analysis reveals the pattern:

First, the function name gives away the purpose and goal of the function. Now, however, the receives and return analysis is backwards. These 'provide ...' functions do not need anything from the calling function in the same way that a function that gets a value from the user does not have any 'receives'. Member functions have access to all the member data in the instance of the class they are associated with. Therefore, 'ProvideSquareFootage', for example, has access to the square footage of any instance it is associated with. (The meaning of 'associated with' must be kept vague for the moment. When we discuss how to use these functions, this will be come clearer.)

With regards to the return analysis: just like a function that gets a value from a user, these functions return one value - the value found in the property they are responsible for. Here then is the design for the first of these functions and the declarations for all three - added after the code discussed above.

    class Contract
    {public:
    	    	   
    	void ChangeSquareFootage(int sqFootage);
    		/*
    		Purpose:	To change the value of the square footage data member
    		Goal:		The square footage data member has a new value
    		Receives	The new Square Footage (an integer)
    		Returns:	NONE
    		*/
    
    	void ChangeNumDesks( int numDesks);
    	// Design comments go here.
    
    
    	void ChangeNumDays( int numDays);
    	// Design comments go here.
    	
    	int ProvideSquareFootage();
    		/*
    		Purpose:	To Return the Square Footage of an Office
    		Goal:		The Square Footage is returned
    		Receives:	NONE
    		Returns:	the Square Footage (an integer)
    		*/
    
    	int ProvideNumberOfDesks();
    	// Design comments go here.
    
    	int ProvideNumberOfDays();
    	// Design comments go here.
    
    	private:
    	.
    	.
    	};
    

There is still one function to go - 'ProvidePerWeekCharge'. We could call it 'CalculatePerWeekCharge' because we decided above to calculate the value each time it is requested. Note, however, that the purpose of the function is to provide the per week charge, regardless of whether we calculate the value each time or not. Thus, the first name is more accurate.

It should also be observed that the decision to always calculate or not is irrelevant to any function that uses this function. All that matters to any function that needs the per week charge is that the charge is delivered correctly. Therefore, we could later decide to change our mind and have a class property that holds the per week charge and carefully update it when needed. Nothing in the rest of the code would need to change. Here again is an example of the power of encapsulation and function decomposition. Changes to one part of a program can be made with little or no effect on the rest of the program.

So, now we have a function called 'ProvidePerWeekCharge' whose purpose is not quite specified by its name. Usually we want a name that indicates exactly what the function does but in this case we have chosen a name that indicates to the user some of what happens while not telling the whole story - and that is OK! In the Purpose and Goal statements, however, we to want to state clearly what the function does:

ProvidePerWeekCharge
Purpose: To calculate and return the per week charge for an office
Goal: The per week charge is returned

We proceed then to the questions involving receives and returns. First, "what does this function need to accomplish its task?" The answer to that is found in the formula: :

    Weekly Charge = Days per Week * ($0.05 per square feet + $5 per desk)

Clearly, the calculation cannot be accomplished unless the function has the Square Footage, the Number of Days Per Week, and the Number of Desks. However, where does it get this information from? To answer this, review where 'ProvideSquareFootage' gets the square footage from. The key here is that all member functions have access to all member data of the instances they are associated with. Thus, the member function 'ProvidePerWeekCharge' has access to the square footage, number of desks, and number of days data for whichever instance the function is associated with. The conclusion then is that 'ProvidePerWeekCharge' has no 'receives' because it has access to all the information it needs.

This is an important point that has been mentioned twice and could bear to be mentioned again.

    Member functions have direct access to the properties of instances of the class they are members of.

An analogy: Fred is a person standing outside a store with a sign that says, "No one under twelve allowed inside unless accompanied by an adult." Whatever 'function' Fred uses to decide whether he can enter the store by himself does not need to 'receive' Fred's age. He knows his own age. Now suppose Fred is eleven years old and the sign says, "No more than three people under twelve allowed inside unless accompanied by an adult." Fred may well not know how many people are already inside the store and, in this case, his 'decision function' will need to somehow receive or ask for this information.

In C++ terms: If we think of the "decision function" mentioned above as a member function of the class 'Person' then we can say that it has access to Fred's age because Fred is an instance of person'. This member function does not, however, have access to the state (the values of all the properties) of the store since the store is not an instance of the class 'Person'.

End of Analogy! Now, what are the returns? If someone asked you to perform some calculation, what do you think they would want back? The result of the calculation, of course! And, that is what this function will return, the calculated weekly charge. Since a 'charge' is a dollar amount, the result is of type double. The design and declaration for this function should be inserted just after the declaration for 'ProvideNumberOfDays' in the declaration for the class 'contract'.

    double ProvidePerWeekCharge();
    /*
    Purpose: To calculate and return the per week charge for an office
    Goal: The per week charge is returned

    Receives: NONE
    Returns: The Per Week Charge (a double)

    */

This completes the declaration of all the member functions for this class and all the elements of the public part of the class.

E. Declaring Member Data - the elements of the Private Part of a Class
As stated above, usually data members are placed in the private part of the class to encapsulate them in hopes of carefully controlling changes. A reminder: the properties of an instance of a class and the data members are the same. Each instance has its own set of property values and thus its own data members. Each data member is really a memory location with the property name being the symbolic name for that memory location. In other words, data members are variables with restricted access. (A third name for data member or property is "instance variable.")

In our case we have three data members. Based on our interface analysis (not explicitly done here) and on common sense, it seems logical to decide that all three will be of type 'int'.

We have seen how local variables belong to and only exist when the function they are local to is called. Data members are variables that belong to and only exist when instances of the class exist . When we declare the member data (and the member functions) of a class we are describing in general terms the properties and behaviors of instances of the class. However, we are NOT creating any specific instances - just as when we describe how "monsters with three heads that live on the far side of the moon" might look and behave, we are not actually creating any such monster. We will see in a bit how to create instances of a class.

The declaration for data members is exactly the same as for any other variable declaration. It should be noted that this time we are dealing with pure declarations and not declarations combined with definitions since memory is not set aside for the data members until an instance is created. Also, note that we have as many memory locations set aside for a certain property declared in a class as there are instances of the given class. In other words, if there are ten contracts then there are ten versions of each of the data members and therefore, for example, there are ten memory locations for square footage.

Since our specifications and design have pretty much determined the type for all the data members, the actual declarations are easy. Here is the completed declaration for the class 'contract'.

    //  Declaration of the class Contract
    // File: contract.h
    
    class Contract
    {public:
    	
    
    	  void ChangeSquareFootage(int sqFootage);
    	  /*
    	  Purpose:	To change the value of the square footage data member
    	  Goal:		The square footage data member has a new value
    	
    	  Receives	The new Square Footage (an integer)
    	  Returns:	NONE
    	  */
    
    	  void ChangeNumDesks( int numDesks);
    	  // Design comments go here.
    
    	  void ChangeNumDays( int numDays);
    	  // Design comments go here.
    
    	  int ProvideSquareFootage();
    	  /*
    	  Purpose:	To Return the Square Footage of an Office
    	  Goal:		The Square Footage is returned
    	  Receives:	NONE
    	  Returns	the Square Footage (an integer)
    	  */
    
    	  int ProvideNumberOfDesks();
    	  // Design comments go here.
    
    	  int ProvideNumberOfDays();
    	  // Design comments go here.
    
    	  double ProvidePerWeekCharge ();
    	   /*
    	  Purpose:	To calculate and return the per week charge for an office
    	  Goal:	        The per week charge is returned
    	  Receives:	NONE
    	  Returns:	The Per Week Charge (a double)
    	  */
    
    	private:
    	  int squareFootage;
    	  int numberOfDaysPerWeek;
    	  int numberOfDesks;
    };
    

F. A Quick Review

  1. There usually are a number of 'Change' functions, each of which has one value it receives- the new value of the data member (property) to be changed - and no values returned;

  2. There are usually a number of 'Provide' functions, each of which receives nothing and returns one value - the value of a specific data member;

  3. All function members that will be used by the rest of the program are public while data members are usually private;

  4. It is common for the public elements of a class be declared first. However, some programmers prefer to code the data member declarations first since knowing the names for the data members helps code the function members. One can meet both goals, of course, by putting the public part of the class first, but coding it after completing the private part.

That finishes the analysis and design phases. It is now time to focus on the how. If we have done our work well, it should be easy to define each member function. In the next section we will test this theory and look at the specifics of member function definitions.

Top of Section Main Menu Next Section