// 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 */