next up previous
Next: 3rd version (Differences between Up: Hardware Store Previous: Original version

2nd version (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;
}


next up previous
Next: 3rd version (Differences between Up: Hardware Store Previous: Original version
Porkoláb Zoltán 2001-09-03