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