About the Code
The code in Listings 1 and 2 was compiled with Microsoft Visual C++ version 5.0 and Borland C++ version 5.02, and complies with the official ISO standard for C++ approved in November 1997. To save space, all functions are defined in situ, i.e., inside their respective class definitions. The code is intentionally not too "bulletproof" so as not to obscure the expression of Alistair's design. I did take some liberties as implementer, however. You will note that I made generous use of the heap via the new and delete operators.
This approach allows for flexible relationships between objects, and reflects the fact that most of the objects in the design are all "first class," i.e., they have a life of their own. This requires a clear policy of who owns which object, so objects get cleaned up properly in destructors. For example, a Recipe is made up of Ingredients, but each Dispenser owns its Ingredient and is responsible for deleting it. In this implementation, the following relationships hold:
CoffeeMachine owns: the Selector, the Cashbox
Selector owns: the DispenserRegister, the ProductRegister
DispenserRegister owns: all Dispensers
ProductRegister owns: all Products
Dispenser owns: an Ingredient
Product owns: a Recipe
Recipe uses: selected Ingredients
I also made use of the containers in the Standard Library where it made sense. For example, Recipe is basically a vector of Ingredients, and ProductRegister uses a vector of Products. Note that the DispenserRegister uses a map to associate Ingredients with their Dispensers for fast lookup when making a Recipe.
Like last month, I use <stdio.h> instead of <iostream>, mainly because the getline function doesn't work as expected with console input. To make the code port between the two compilers, the following unusual "features" appear:
* DispenserRegister::dispenserOf could not be made const, although it should be (Visual C++ didn't like it).
* The map defined in DispenserRegister needs the third template argument, less<const Ingredient*, Dispenser>, because Borland doesn't support default template arguments and didn't provide overloads as a work-around.
* In ~DispenserRegister I would have used p->second instead of (*p).second, but Borland gets confused (the ++ is immaterial for this issue). Chuck Allison o