Notes for week 7

For this week's links click here.

The Employee Problem Continued.

Last week we discussed the design of the employee payroll problem . In this simple implementation of the program, I just added a typedef for the Array< EmployeePtr> to the list of classes in the employee header . The implementation of this program is an interesting example and introduces some new Builder techniques.

Review The Design and Code

At this point you have a working design for the problem, but it is wise to review your design from time to time to see if it could be improved. The first thing to note is that the use of typedef to create the array of employee, while quick, only creates a general array, not one suited to the needs of this problem. In the paint function for instance, we could have created cleaner code by calling a print routine for EmployeeArray. We did this in our grocery list problem . When you implement the open, save and saveas functions, it is much easier if you create read and write functions for the EmployeeArray. Finally, special functions like that creating a payroll list are easier to create in the class EmployeeArray.

At first glance it would seem to be a difficult problem.You have used many properties of an array (such as the subscript operator [], the insertItem, insertItemAt and deleteAt functions) in our code. Will you have to redefine all these functions? What if you miss one? The conversion from the typedef to a class is not as painful as it might seem. All that is necessary is to make the following declaration.

class EmployeeArray :public Array< EmployeePtr>
{
public:
   void print(ostream&);
   void write(ostream&);
   void read(istream&);
};

Since this class is a public descendent of Array, all of the functions and operators for an array are available for members of the class. This is a problem with this design that can haunt you. The subscript operator is not designed to work with functions like insertItem, so a programmer other than the designer of the EmployeeArray, might misuse this class. To maintain encapsulation you should ask yourself the following question before creating a descendent class.

Are all of the functions for the parent class either implemented by this class or useful for this class?

If the answer to this question is no, do not make the new class a descendent of the the perspective parent. Instead encapsulate the class as a data member of your new class. In this case create the following class. Then add only the functions you will need to the interface.

class EmployeeArray
{
public:
   void insertItem(EmployeePtr);
   void insertItemAt(EmployeePtr, int);
   void deleteItem(EmployeePtr);
   void deleteItemAt(int);
   int find(EmployeePtr); 
   void print(ostream&);
   void write(ostream&);
   void read(istream&);
private:
   Array< EmployeePtr> _employee;
};

A sampling of the trivial functions:

void EmployeeArray::insertItem(EmployeePtr p)
{
   _employee.insertItem(p);
}
int EmployeeArray::find(EmployeePtr p)
{
	return _employee.find(p);
}

Once you have created an array of EmployeePtr, you need to have an == operator for this item. Here you want all the functions of the RcPointer class as they stand, so you can make EmployeePtr a class that is a public descendent of RcPointer< Employee>.

class EmployeePtr :public RcPointer< Employee>
{
public:
   int operator == (const EmployeePtr & e) {return e->lastName() == lastName();}
}; 

Here you use the last name as a test for equality. It is available since all types of employee have a virtual function lastName. You should make these changes in the code you have downloaded ( Exam6.zip ), and add the functions to open, save and saveas to the program. Check the grocery problem to see how this is done.

The Grocery List - Modified

What changes would you have to make to the grocery list problem if some of the items were taxable? Could you just mark some things as taxable? This would lead to a set of problems not the smallest of which is when do you compute the tax. If you compute the tax at the time when you add the cost to our total, you would have to use the amount of the sale twice. Remember, that each object is supposed to know how to "do its thing." That means each object should compute its own tax. This means that the function computeTax would have to be available for each object, but would return 0 for nontaxable items, while computing the tax on taxible items.

Clearly computeTax is work for a virtual function and you really have two types of GroceryItem,

If you have two types of GroceryItem, and you mix these items in the same list, you must use pointers to the items because of strict typing. Now you have an array of items, so you must produce an array of (counted) pointers to objects that are children of GroceryItem.

Designing the Children For The Grocery Class

You already have a GroceryList implemented, so it would be nice to reuse the much of the code we already have. In this case the only new function is the one that computes the tax. This does not mean some of the other functions will not change from class to class. In this case the first thing to do is convert all the functions to virtual functions. You should also add the enumerated values TAXABLE and NONTAXABLE to the GroceryItem. One last change in the base class is to create a section of the class labeled protected. Anything declared in this section is only available to children of the given class. If you put the constructors for GroceryItem in this section, no program can create a GroceryItem. This restricts all GroceryItems to either be Taxable or NonTaxable, but allows constructors for these two classes to initialize the parent. This is important since all of the data items except the tax rate will remain as part of the GroceryItem.

class GroceryItem
{
public:
	GroceryItem();
	GroceryItem(String name,String id,double cost, int howMany);
	double computeCost();
	void print(ostream &);
	void printShort(ostream &);
	void write(ostream &);
	void read(istream &);
	int operator ==(const GroceryItem &)const;
private:
	String readName(istream&); //needed to read all of item name.
	String _name;
	String _idNumber;
	double _unitCost;
	int _sizeOfOrder;
};

class GroceryList
{
public:
	GroceryList();
	double computeCost();
	void addItem(const GroceryItem &newItem);
	void deleteItemAt(int i);
	void print(ostream &);
	void print(int,ostream&);
	void write(ostream &);
	void read(istream &);
	void clearList();
private:
	Array< GroceryItem> _item;
};

The Revised Class Definitions

Notice while some of the virtual functions had to be redefined for both classes (such as computeTax) other functions such as read are not redefined at all. Read from GroceryItem will work for both types of grocery item since all data is contained in the base class. The functions that print and write are different because they must include information about the type of object. The strange case is the computeCost function. You will need a computeCost function in GroceryItem because you have no way to access _unitCost or _sizeOfOrder in the descendent classes. This function works well for NonTaxable items, so the class NonTaxable does not override the computeCost function. The major differnence in the GroceryList is that it contains GroceryPtr items. This requires minor changes throughout the class. Notice the tax rate is declared as a static value in the Taxable class. This type of variable is shared by all members of the class. This not only saves space it makes it easier to update the tax rate. The value is assigned in the cpp file (see below).

class GroceryItem
{
public:
    enum {TAXABLE, NONTAXABLE};
    virtual double computeCost();
    virtual void print(ostream &);
    virtual void printShort(ostream &);
    virtual void write(ostream &);
    virtual void read(istream &);
    virtual double computeTax()=0;
    int operator ==(const GroceryItem &)const;
protected:  
    GroceryItem();
    GroceryItem(String name,String id,double cost, int howMany);
private:
    String readName(istream&); //needed to read all of item name.
    String _name;
    String _idNumber;
    double _unitCost;
    int _sizeOfOrder;
};

typedef RcPointer< GroceryItem> GroceryItemPtr;

class Taxable :public GroceryItem
{
public:
    static double TaxRate;
    Taxable(){}
    Taxable(String name,String id,double cost, int howMany);
    virtual double computeTax(); 
    virtual double computeCost();  
    virtual void print(ostream &);
    virtual void printShort(ostream &);
    virtual void write(ostream &);
};
class NonTaxable :public GroceryItem
{
public:
    NonTaxable(){}
    NonTaxable(String name,String id,double cost, int howMany);
    virtual double computeTax();  
    virtual void print(ostream &);
    virtual void printShort(ostream &);
    virtual void write(ostream &);
};

Some of the Functions

The member functions of GroceryItem are unchanged, but the functions for the other two classes have some interesting twists. The member functions for Taxable show you the general idea of using a base class which actually contains data. Notice that most of the data oriented operations are carried out by functions in GroceryList. Even the computeTax function that seems to be independent of the base class, must call on computeCost to get the amount on which the tax is computed.

double Taxable::TaxRate = 0.05;
Taxable::Taxable(String name,String id,double cost, int howMany)
    :GroceryItem(name,id,cost,howMany)
{}
double Taxable::computeTax()
{
    double result = GroceryItem::computeCost();
    return result * TaxRate;
}

double Taxable::computeCost()
{
    double result = GroceryItem::computeCost();
    result += result * TaxRate;
    return result;
}
void Taxable::print(ostream &out)
{
    GroceryItem::print(out);   
    out<<"\t\tTaxable\tTax: $"<

Complete Code

The interface for this program is similar to the interface for example 5 you have to add radio buttons to the NewGroceryItem dialog so you can indicate whether the item is taxable or not. To get the code click here .