The Grocery List

The Problem

We want to construct a grocery list program that will allow us to create a grocery list. The list will be created over a period of time and just before a trip to the store the program can print the list. Items will be added to the list ( if you want to order more of an item, you will simply add another list item with the additional order not edit the existing record), and stored in a file between times when you run the program. When the user enters a new item they will enter the number needed and the unit price of the item. The program will print the list (to a window) with the total cost of the items on the list.

Object-Oriented Analysis of the Problem

First we want to decide on the objects involved in this program. Make a list of the nouns in the paragraph that are associated with the problem.

  1. grocery list
  2. time period
  3. store
  4. item - grocery item
  5. file of items
  6. number needed
  7. unit price
  8. total cost.

Not all of these nouns will be modeled in the program, but all things modeled are here (with the exception of interface requirements such as menu). We will delete time period since that is determined by the user. Similarly store is not essential to this problem. The file of items is both an ifstream and an ofstream at different times, but the system provides the functions needed. The number needed and unit price are just numbers belonging to grocery item and total cost is output from the grocery list. This means we will have to create the class GroceryList and the class GroceryItem.

First Sketch of GroceryList

In object oriented design the first thing that we use to characterize a class is the actions it can do. Again we go back to the problem. This time we look for verbs that are connected with the grocery list. We have read from a file, store in a file, add to list, and print. This means that the public view of grocery list will be:

class GroceryList
{
public:
	GroceryList();// At least one constructor
	void read(istream &); // read from a file
	void write(ostream &); // write to the file
	void print(); // print the list to the screen
	void addItem(); // add an item to the list 
private:
// The actual structure of the data storage is not important at this point
};

First Sketch of GroceryItem

At first the only obvious functions needed in GroceryItem are read, write and print.

class GroceryItem
{
public:
	GroceryItem(); \\ Constructors as needed
	void read(istream &);
	void write(ostream &);
	void print();
};

As we will see, sometimes when we start creating functions we will find that our first guess as to what functions are needed is not always complete. One tool used to show the relationship between classes and to discover additional functions is the Scenario. We will use the GroceryList function print to illustrate our first scenario.

From this scenario we see that we need a function to compute the total cost of the grocery list. The scenario for this function uncovers the need for a function computeCost for GroceryItem. One the other hand there is no need to create a function to print the name of the grocery item, its unit cost, number of items or the total cost since these are all predefined objects (name is a string) or primitives (int or double).

New Aspects

This problem has some aspects we have not encountered before, they are:

The Structure of Grocery List

Consider the last item first. The library of container classes that all modern c++ compilers has containers for many different purposes. The simplest list is an array class. The Borland array class has some flaws, so we will us my array class. If you have already looked at the file darray.h, you will have noticed that there are four classes defined in the file, TypeRep, Array, ArrayProcess and ArrayIterator. TypeRep is very similar to our counted pointer and is used for memory management. ArrayProcess is used for some fancy work on an array and will be discussed later in the course. We will only use Array and ArrayIterator in this project and for some weeks to come You will also notice some funny notation involving the word template. Templates allow us to define functions for a container once and use them on many different types of objects. Array could be an array of integers or an array of grocery items.

Next week we will worry about coding this problem. For now all we are interest in is the structure of the solution. We can model the problem in two ways, inheritance or containment.

Inheritence

Containment

As you have seen earlier, if a class has the properties of another class, we can make one class a child of the other. This is inheritance-the IsA relation. In this case we might think that since Array has functions like insertItem, we might want to make our GroceryList an Array. The first problem is that GroceryList does not have a deleteItem, so we would give GroceryList functions it doesn't have. Second Array knows nothing about cost, so GroceryList has more properties than an Array. The simplest way to handle this problem is to make one of the member data elements of GroceryList an Array. This is containment - the HasA relation. We will revisit the isa versus hasa relationship often.

Intermediate Version of the Classes

One function that wasn't evident from our discussion was a function to clear the list once we used it. We add clearList to the interface and get the following class definitions.

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

and

class GroceryItem
{
public:
	GroceryItem();
	GroceryItem(string name,string id,double cost, int howMany);
	double computeCost();
	void print(ostream &);
	void write(ostream &);
	void read(istream &);
private:
};