First Next Previous Last Glossary About

Compounding objects that is, Composite Classes - Part 1

Introduction

#include <iostream>
#include <cstdlib>

class compClass
{ public:
   compClass()  { cout << "Creating compClass\n"; }
   ~compClass() { cout << "Deleting compClass\n"; }
   void setInt(int x)  { compInt = x; }
   void showInt(void)  { cout << compInt << endl; };
  private:
   int compInt;	
};

class mainClass
{ public:
   mainClass()  { cout << "Creating mainClass\n"; }
   ~mainClass() { cout << "Deleting mainClass\n"; } 
   void setInt(int x) { mainInt = x; };
   void showInt(void) { cout << mainInt << endl; };
   compClass cC;
  private:
   int mainInt;
};


int main(void)
{ mainClass x;

  x.setInt(100);   x.cC.setInt(99);
  x.showInt();     x.cC.showInt();
  
  return EXIT_SUCCESS;
}

A class can include members that are themselves classes and this is characteristic of the real world where an object is a composite of many other objects.

Here you can see that there are compClass and mainClass and that mainClass has a public member called cC which is of type compClass. The various member functions are declared inline.

The class mainClass is a composite class, composed of itself and another class.

Here is the output:

Creating compClass
Creating mainClass
100
99
Deleting mainClass
Deleting compClass

This shows the order in which the constructors and destructors are called.

The next example is a little more detailed but shows the same thing using our TV example.



class TPowerSupply
{ public:
   powertype GetSupplyType()
    { return supply; }
   void SetSupplyType(powertype p)
    { supply = p; }
  private:
   powertype supply;
};

class TVModel
{ public:
   void  OnOff();
   toggleswitch IsOn();
   void InitTV(toggleswitch,int,int,int,
               int,int,powertype);
   void AdjustVolume(int Level);
   int  GetVolume();
   powertype GetSupplyType()
    { return supply.GetSupplyType(); }
   static int GetNumberTVs()
    { return nNumber; }

  protected:
   toggleswitch bOnOffSwitch;
   int nVolumeLevel,
       nChannelNumber,
       nColour,
       nBrightness,
       nContrast;
   static int nNumber;
   TPowerSupply supply;

  private:
};

The TVModel class contains a protected member whch is a TVPowerSupply class and there is a public method - powertype GetSupplyType() - which can return the kind of power supply that the TV has.

The methods in both TVModel and TPowerSupply are all declared inline, eg:

powertype GetSupplyType()
{ return supply.GetSupplyType(); }

Note how the messages are used. We don't send a message directly to the power supply to get it to return it's type. The message is sent to the TV, after all the TV 'owns' the power supply.

There is no constructor nor destructor declared, all the initialisation will be done by InitTV().

A complete example program using these classes follows. I have described the details for you and after you have read the description and the program you should do the tutorial work that follows.



#include <iostream>
#define MAXLEVEL 100
#define MINLEVEL 0
Since we will use cout we'd better include the interface. MAXLEVEL and MINLEVEL are constants for some of the TV controls.
typedef enum toggleswitch {OFF = 0, ON = 1};
typedef enum powertype    {LINEAR, SWITCHED, SOLAR, 
                           LEADACID, STEAM};

const char *SupplyDesc[] = {"Linear", "Switched", "Solar", 
                            "Lead Acid", "Steam"};
const char *PS = "Power supply is ";
The enumerated ranges which are very useful both as a self-documenting programming technique and as an aid to the revealing the semantics of the program code. The string array, SupplyDesc, is declared so that it might be used to display a string representation of an object's power supply type. Both of the string structures are declared as constants.
The classes are as above.
void TVModel::OnOff()
{if (bOnOffSwitch == ON)
  bOnOffSwitch = OFF;
 else
 if (bOnOffSwitch == OFF)
  bOnOffSwitch = ON;
}

toggleswitch TVModel::IsOn()
{ return bOnOffSwitch; }

void TVModel::InitTV(toggleswitch t, int v, int cn, 
                     int col, int b, int c, powertype p)
{ bOnOffSwitch    = t;
  nVolumeLevel    = v;
  nChannelNumber  = cn;
  nColour         = col;
  nBrightness     = b;
  nContrast       = c;
  supply.SetSupplyType(p);
  nNumber++;
}

void TVModel::AdjustVolume(int Level)
{ if (IsOn())
   if ((Level < MAXLEVEL) && (Level > MINLEVEL))
    nVolumeLevel = Level;
}

int TVModel::GetVolume()
{ return nVolumeLevel;}

int TVModel::nNumber = 0;
Here are the implementations of the class methods.
void msgCount(TVModel, const char *);
void msgInfo(TVModel);
The prototypes for some local functions.
int main()
{  TVModel Sony, Akai, DRB;
   Sony.InitTV(OFF,10,5,4,3,2, LINEAR); 
   Akai.InitTV(OFF,23,42,0,0,0, SWITCHED);
   DRB.InitTV(OFF,100,100,0,0,0, STEAM); 
   msgCount(DRB,"We have ");
   msgInfo(Sony);
   msgInfo(Akai);
   msgInfo(DRB);
   return 0;
}
The main function declares three TV instances which are then initialised with the InitTV member function.
void msgCount(TVModel T, const char * mmsgCount)
{cout << mmsgCount << T.GetNumberTVs() 
      << " tv's " << endl;
}

void msgInfo(TVModel T)
{
 cout << T.GetVolume() << " "
      << PS 
      << SupplyDesc[T.GetSupplyType()] 
      << endl;
}


Tutorial 1

Here is some tutorial work for you.

1 Try the example first to see that it works.

2 A tv also has case which is, nowadays, usually made of black plastic and marked with symbols which are both difficult to see and to understand. Add a class called TCaseType which has public member functions SetCaseType and GetCaseType and a private member data field called CaseMaterial.

The data field CaseMaterial should be type casetype which is an enumerated range PLASTIC, CARDBOARD, MASONITE. There should be a string array which contains string representations of the enum.

Modify the msgInfo function to add the display of the tv case type.



Nesting classes - confusion reigns

#include <stdio.h>
#include <cstdlib>

class mainClass
{ public:
   mainClass()  { printf("Creating mainClass\n"); };
   ~mainClass() { printf("Deleting mainClass\n"); };
   
   void setInt(int x)     { mainClassInt = x; }
   void showInt(void)     { printf("mainClass -> %d\n", mainClassInt); }
   void setpubInt(int x)  { pu.setInt(x); }
   void showpubInt(void)  { pu.showInt(); }
   void setprivInt(int x) { pr.setInt(x); }
   void showprivInt(void) { pr.showInt(); }
   
   class pubComp
    { public:
       pubComp();
       ~pubComp();
       void setInt(int x) { pubCompInt = x; }
       void showInt(void) { printf("pubComp -> %d\n", pubCompInt); }
      private:
       int pubCompInt;
    };

   pubComp pu;
  
  private:
   int mainClassInt;
   
   class privComp
    { public:
       privComp();
       ~privComp();
       void setInt(int x) { privCompInt = x; }
       void showInt(void) { printf("privComp -> %d\n", privCompInt); }
      private:
       int privCompInt;
    };
   privComp pr;
};

mainClass::pubComp::pubComp() { printf("Creating pubComp\n"); };
mainClass::pubComp::~pubComp() { printf("Deleting pubComp\n"); };
mainClass::privComp::privComp() { printf("Creating privComp\n"); };
mainClass::privComp::~privComp() { printf("Deleting privComp\n"); };

int main(void)
{ printf("Outer Block\n");
  mainClass outer;
  outer.setInt(100);
  outer.showInt();
  outer.setpubInt(222);
  outer.showpubInt();
  outer.setprivInt(111);
  outer.showprivInt();
  
  printf("Inner Block\n");
  { mainClass inner;
    inner.setInt(1000);
    inner.showInt();
  }
  printf("END Inner Block\n");

  return EXIT_SUCCESS; //EXIT_SUCCESS from cstdlib
  printf("END Outer Block\n");
}


Outer Block
Creating pubComp
Creating privComp
Creating mainClass
mainClass -> 100
pubComp -> 222
privComp -> 111
Inner Block
Creating pubComp
Creating privComp
Creating mainClass
mainClass -> 1000
Deleting mainClass
Deleting privComp
Deleting pubComp
END Inner Block
Deleting mainClass
Deleting privComp
Deleting pubComp

If you take a close look at the example above you will see it is a class (mainClass) which contains two other classes pubComp and privComp.

This isn't a usual case of composition since pubComp and privComp are declared inside mainClass. I haven't declared instances of these. In the earlier examples I declared classes and then declared an instance of the class in the composite class.



Nested and Composite Classes

The diagram shows how the composite structure differs from the nested structure.

At the moment I can't see the point of nested classes, it seems to fly in the face of everything that we aim to do with OO design. I can't see what you can do with nested classes that you cannot do with composite classes or inheritance. But ... my experience with C++ is limited and perhaps I am missing something.

For example ... We have a modelled a TV which has a power supply. The power supply is in its own right an object which a TV uses. It's common practice to use the same kinds of power supply in different kinds of electronic equipment. If the TV was modelled with a nested power supply class then we have lost the benefit being able to reuse the class.

Anyway C++ permits such constructions and I have told you about them. I have done my duty.

First Next Previous Last Glossary About


Copyright © 1999 - 2001 David Beech