Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

Object-Oriented Analysis and Design, Part 2


June 1998/Object-Oriented Analysis and Design, Part 2/Listing 1

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

Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.