/*
    Finding a Common Abstraction

    Motto: Concentrate common abstractions in a base class.

    The class interfaces of Card and Monitor are similar: Both have 
    price() and name() as pure virtual functions. The difference is 
    that class Card has an additional virtual function: rebate().
    Studying the similarities and differences between these classes
    will clarify their relationships and lead to a better program.

    Card and Monitor are similar, but they are not formally related 
    in the inheritance hierarchy. Both Card and Monitor have price()
    and name() memberfunctions. They are both computer components. 
    It makes mor esense to formalize that common abstraction in a 
    further base class called: Component. This makes the semantic 
    of a computer explicitelly defined with the help of C++ language
    tools.
    
 */

#include <iostream>
using namespace std;

enum CARD       {CDROM, TAPE, NETWORK };
enum MONITOR    { MONO, COLOR };

class Component
{
public:
    virtual int     price() = 0;
    virtual char   *name() = 0;
    virtual int     rebate();
            int     netPrice();
};

/*
   Although Component adds another class to the program, it unifies
   Card and Monitor by identifying a common base abstraction. With 
   the addition of Component, the program better models the program 
   domain.
   
   As you can read from the first implementation a card may have a
   rebate but a Monitor object may not. Is the distintion intentional,
   or merely accidental? It is better to chose a general approximation
   here, therefore we suppose that a Component may have a rebate. We
   express this to declare rebate() as a member of Component.
   
 */

int Component::rebate()
{
    return 0;
}

/*
   Although price() and name() in Component are pure virtual functions, 
   Component::rebate() is not. There is no meaningful default default
   implementation of price() and name() that the base class might use 
   - that information must be specified by a derived class before an
   object may be instatiated. In contrast, a rebate of zero is a sound
   default implementation to use in a base class.
   
 */ 

class Card : public Component
{
public:
    virtual int     price() = 0;
    virtual char   *name() = 0;
    virtual int     rebate();
};

class Monitor : public Component
{
public:
    virtual int     price() = 0;
    virtual char   *name() = 0;
};

class Network : public Card
{
public:
    int     price();
    char   *name();
};

class Tape : public Card
{
public:
    int     price();
    char   *name();
};

class CDRom : public Card
{
public:
    int     price();
    char   *name();
    int     rebate();
};

class Color : public Monitor
{
public:
    int     price();
    char   *name();
};

class Monochrome : public Monitor
{
public:
    int     price();
    char   *name();
};

int   Card::rebate()  { return 45; }

int   Network::price()    { return 600; }
char *Network::name()     { return "Network"; }

int   CDRom::price()      { return 1500; }
char *CDRom::name()       { return "CDRom"; }
int   CDRom::rebate()     { return 135; }

int   Tape::price()       { return 1000; }
char *Tape::name()        { return "Tape"; }

int   Color::price()      { return 1500; }
char *Color::name()       { return "Color"; }

int   Monochrome::price() { return 500; }
char *Monochrome::name()  { return "Mono"; }

class Computer
{
public:
            Computer( CARD, MONITOR );
           ~Computer();
    int     netPrice();
    void    print();
private:
    Card    *card;
    Monitor *mon;
};

Computer::Computer( CARD c, MONITOR m )
{
    switch( c )
    {
        case NETWORK:   card = new Network;     break;
        case CDROM:     card = new CDRom;       break;
        case TAPE:      card = new Tape;        break;
    }
    switch( m )
    {
        case MONO:      mon = new Monochrome;   break;
        case COLOR:     mon = new Color;        break;
    }
}

Computer::~Computer()
{
    delete card;
    delete mon;
}

/*
    Unification of the base abstractions (creating the common base
    class Component permits a simplification of the pricing. Both
    Card and Monitor have a rebate, so we can implement netPrice()
    in Component:

 */

int   Component::netPrice()
{
    return price() - rebate();
}

/*
    As a consequece we can drastically simplify the netPrice() of 
    Computer. It is the sum of netPrice()-es of the Components.
    
 */
int   Computer::netPrice()
{
    //  was: return mon->price() + card->price() - card->rebate();
    return mon->netPrice() + card->netPrice();
}

void    Computer::print()
{
    cout << "Configuration = " << card->name() 
         << ", "               << mon->name()  
         << ", net price = "   << netPrice() 
         << endl;
}

int main()
{
    Computer nm( NETWORK, MONO );
    Computer cm( CDROM,   MONO );
    Computer tm( TAPE,    MONO );
    Computer nc( NETWORK, COLOR );
    Computer cc( CDROM,   COLOR );
    Computer tc( TAPE,    COLOR );

    nm.print();
    cm.print();
    tm.print();
    nc.print();
    cc.print();
    tc.print();

    return 0;
}