Listing 1: Coffee Machine Design 4
// coffee4.h #include <stdio.h> #include <vector> #include <map> #include <algorithm> #include <string> #include <assert.h> using std::string; // class Ingredient: // Abstraction of a simple ingredient. // Knows its name only class Ingredient { public: Ingredient(const string& name) : myName(name){} string name() const {return myName;} private: string myName; }; // class Dispenser: // Abstraction of a dispenser of an ingredient. // Controls dispensing, tracks amount left. class Dispenser { public: Dispenser(Ingredient* pIngredient, int shots) : contents(pIngredient) { assert(pIngredient); shotsLeft = shots; } ~Dispenser() {delete contents;} const Ingredient* contains() const {return contents;} void add(int shots) {shotsLeft += shots;} void dispense(int shots) { printf("Dispensing %d shot(s) of %s\n", shots, contents->name().c_str()); shotsLeft -= shots; assert(shots >= 0); } int shotsAvailable() const {return shotsLeft;} private: const Ingredient* contents; int shotsLeft; }; // class DispenserRegister: // Abstraction of the thing that knows all the dispensers. // Acts as a librarian for the dispensers, controls nothing. class DispenserRegister { public: ~DispenserRegister() { map_type::iterator p = dispensers.begin(); while (p != dispensers.end()) delete (*p++).second; } void addDispenser(Dispenser* pDispenser) { assert(pDispenser); dispensers[pDispenser->contains()] = pDispenser; } Dispenser* dispenserOf(const Ingredient* pIngredient) { assert(pIngredient); return dispensers[pIngredient]; } private: typedef std::map< const Ingredient *, Dispenser*, std::less<const Ingredient* > > map_type; map_type dispensers; }; // class Recipe: // Abstraction of a recipe. // Tells the dispensers to dispense ingredients in sequence. class Recipe { public: Recipe(const Ingredient* pI1, const Ingredient* pI2, const Ingredient* pI3, const Ingredient* pI4 = 0, const Ingredient* pI5 = 0) { assert(pI1); addIngredient(pI1); assert(pI2); addIngredient(pI2); assert(pI2); addIngredient(pI2); if (pI4) addIngredient(pI4); if (pI5) addIngredient(pI5); } void addIngredient(const Ingredient* pIngredient) { ingredients.push_back(pIngredient); } void makeOn(DispenserRegister* pDispenserRegister) { for (int i = 0; i < ingredients.size(); ++i) pDispenserRegister->dispenserOf(ingredients[i]) ->dispense(1); } private: std::vector<const Ingredient*> ingredients; }; // class Product: // Abstraction of the drink. // Responsible for knowing its price and recipe. class Product { public: Product(const string& name, int price, Recipe* recipe) : myName(name) { myPrice = price; myRecipe = recipe; } ~Product() {delete myRecipe;} string name() const {return myName;} int price() const {return myPrice;} Recipe* recipe() const {return myRecipe;} void makeOn(DispenserRegister* pDispenserRegister) { myRecipe->makeOn(pDispenserRegister); } private: string myName; int myPrice; Recipe* myRecipe; }; // class ProductRegister: // Abstraction of the thing that holds all the products. // Knows what products are available. class ProductRegister { public: ~ProductRegister() { // Need to destroy all products: for (int i = 0; i < products.size(); ++i) delete products[i]; } Product* productFromIndex(int index) const { if (index < 0 || index >= products.size()) throw "Invalid product index"; return products[index]; } void addProduct(Product* pP) {products.push_back(pP);} private: std::vector<Product*> products; }; // class Cashbox: // Abstraction of a change maker or cashbox on a real machine. // Responsible for knowing how much credit the customer has, // making change, accepting coins. // This version is suited for, but does not include credit cards. class Cashbox { public: Cashbox() {credit = 0;} void deposit(int amount) { credit += amount; printf("Depositing %d cents. You have %d cents credit.", amount, credit ); } void returnCoins() { if (credit > 0) { printf("Returning %d cents.", credit); credit = 0; } } bool haveYouFor(const Product* choice) const { return credit >= choice->price(); } void deductFor(const Product* choice) { credit -= choice->price(); returnCoins(); } private: int credit; }; // class Selector: // Abstraction of the internal selector and controller. // Knows products & selection, coordinates payment and drink making. class Selector { public: Selector(Cashbox* pC, ProductRegister* pP, DispenserRegister* pD) { pCashbox = pC; pProductRegister = pP; pDispenserRegister = pD; } ~Selector() { delete pDispenserRegister; delete pProductRegister; } void select(int choiceIndex) { Product* pProduct = pProductRegister->productFromIndex(choiceIndex); if (pCashbox->haveYouFor(pProduct)) { pProduct->makeOn(pDispenserRegister); /* We omit here, for space, rebuilding the list of * legitimate selections, and checking for empty dispensers. * Selector should ask the ProductRegister, which asks each * product to check all its ingredients: * pProductRegister.checkQuantities * (pDispenserRegister) */ pCashbox->deductFor(pProduct); } else puts("Sorry, you need more money. No drink."); } private: Cashbox* pCashbox; ProductRegister* pProductRegister; DispenserRegister* pDispenserRegister; }; // class CoffeeMachine: // Abstraction of the outer machine, holding all the parts. // Responsible for constructing machine, capturing external input. class CoffeeMachine { Cashbox* pCashbox; Selector* pSelector; public: CoffeeMachine() { DispenserRegister* pDispenserRegister = new DispenserRegister; ProductRegister* pProductRegister = new ProductRegister; pCashbox = new Cashbox; pSelector = new Selector(pCashbox, pProductRegister, pDispenserRegister); // Load-up the ingredients - normally would // obtain externally: Ingredient* cup = new Ingredient("cup"); Ingredient* coffee = new Ingredient("coffee"); Ingredient* creamer = new Ingredient("creamer"); Ingredient* sugar = new Ingredient("sugar"); Ingredient* water = new Ingredient("water"); Ingredient* bouillionPowder = new Ingredient("bouillion powder"); pDispenserRegister->addDispenser(new Dispenser(cup, 30)); pDispenserRegister->addDispenser(new Dispenser(coffee, 10)); pDispenserRegister-> addDispenser(new Dispenser(creamer, 10)); pDispenserRegister->addDispenser(new Dispenser(sugar, 10)); pDispenserRegister-> addDispenser(new Dispenser(bouillionPowder, 10)); pDispenserRegister->addDispenser(new Dispenser(water, 30)); // Load-up the Products - normally would obtain externally: Product* black = new Product("black", 35, new Recipe(cup, coffee, water)); Product* white = new Product("white", 35, new Recipe(cup, coffee, creamer, water)); Product* sweet = new Product("sweet", 35, new Recipe(cup, coffee, sugar, water)); Product* whiteSweet = new Product("whiteSweet", 35, new Recipe(cup, coffee, sugar, creamer, water)); Product* bouillion = new Product("bouillion", 25, new Recipe(cup, bouillionPowder, water)); pProductRegister->addProduct(black); pProductRegister->addProduct(white); pProductRegister->addProduct(sweet); pProductRegister->addProduct(whiteSweet); pProductRegister->addProduct(bouillion); } ~CoffeeMachine() { delete pSelector; delete pCashbox; } bool oneAction() { char cmdline[BUFSIZ]; char action[BUFSIZ]; int value; puts("\n______________________________________"); puts ("\tPRODUCT LIST: all 35 cents, except bouillion (25 cents)"); puts ("\t1=black, 2=white, 3=sweet, 4=white & sweet, 5=bouillion"); puts ("\tSample commands: insert 25, select 1. Your command:"); gets(cmdline); sscanf(cmdline, "%s %d", action, &value); if (strcmp(action,"insert") == 0) pCashbox->deposit(value); else if (strcmp(action,"select") == 0 && value>=1 && value<=5) pSelector->select( value-1 ); else if (strcmp(action,"quit") == 0) return false; else printf (" Did not understand request %s %d.\n",action,value); return true; } }; /* End of File */