next up previous
Next: Vehicles and Garages Up: Hardware Store Previous: 3rd version (Differences between

Final version (The Role of a class)

Motto: a public derived class should be a specialization of its base class


In one respect, the program has lost ground in the transformation. The original program specified only once, in Card::rebate(), that the default rebate for cards i s45. With the current definition of Component, the values of all non-zero rebates must be specified in the object declarations. The program has no place to record a default rebate specifically for cards. The program does need to distinguish cards from monitors. Inheritance can provide appropriate specialization with distinct constructors for cards and monitors.

We must reintroduce the classes Card and Monitor to provide constructors with the appropriate rebate defaults.

The specialization in the derived classes is limited to their constructors. The default rebates are specified as default argument values to the constructors for Card and Monitor.

#include <iostream>
using namespace std;

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

class Component
{
public:
        Component( int p, char *n, int r = 0);
    int     netPrice();
    int     price()  { return m_price; }
    char   *name()   { return m_name; }
    int     rebate() { return m_rebate; }
private:
    int     m_price;
    char   *m_name;
    int     m_rebate;
};

Component::Component( int p, char *n, int r)
                : m_price(p), m_name(n), m_rebate(r)  { } 

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

class Card : public Component
{
public:
    Card( int p, char *n, int r = 45)
		: Component( p, n, r)  {   }
};

class Monitor : public Component
{
public:
    Monitor( int p, char *n, int r = 0)
		: Component( p, n, r)  {   }
};

Generally, public inheritance is used when the derived class is a specialization of the base class, that is, when the classes exhibit the "is a kind of" relationship. A Card is a kind of Component; a Monitor is a kind of Component. In this case, the specialization applies only during construction - once constructed, all Component objects behave uniformly. Limiting the variation in the derived classes to initialization - constructor specialization - is a legitimate use of inheritance.

It is interesting to note, that Card and Monitor now differ in a "default value", implemented as a constructor default argument value. In the original version of the program, Card and Monitor differed in a "default behaviour", implemented as a virtual function.

Card    Network(600, "Network");
Card    CDRom(1500, "CDRom", 135);
Card    Tape(1000, "Tape");
Monitor Color(1500, "Color");
Monitor Monochrome(500, "Mono");

In place of five Component objects, there are now three Card objects and two Monitor objects. Only one rebate value need be specified; only the CDRom object deviates from its default.

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

The reintroduction of Card and Monitor can simplify the program in other way. The arguments to Computer::Computer specify one CARD and one MONITOR value. The enumerations place a level of indirection between the constructor and the information it needs, without adding any flexibility. The constructor really needs the Component objects and has to map each enumeration value to an object.

Maintaining this mapping is complicated. To add another Monitor for example GreyScale, the programmer has to

Card and Monitor are distinct types. If Computer::Computer takes pointers to objects as its arguments, the enumerations and the switch statements can be removed.

Computer::Computer( Card *c, Monitor *m )
{
    card = c;
    mon  = m;
}

int   Computer::netPrice()
{
    return mon->netPrice() + card->netPrice();
}

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

Here you must modify the client code: you construct Computer with the addresses of the objects rather than the enumerators

int main()
{
    Computer nm( &Network, &Monochrome );
    Computer cm( &CDRom,   &Monochrome );
    Computer tm( &Tape,    &Monochrome );
    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: Vehicles and Garages Up: Hardware Store Previous: 3rd version (Differences between
Porkoláb Zoltán 2001-09-03