Aggregation
Good OO design and implemenation focus on
code reuse. How can/should code be designed
and implemented so that existing code can be
reused without modification? That's one of
the strengths of OO languages like C++.
Aggregation (aka composition)
is a common technique for code reuse. Simply put, aggregation
is using one class as a data member of another class.
This technique is used when the classes have a "has a" relationship.
Consider this simple example.
Recall our now familiar DayOfYear class.
#ifndef DAYOFYEAR_H
#define DAYOFYEAR_H
class DayOfYear
{
public:
DayOfYear ( int initialMonth = 1, int initialDay = 1 );
void Set( int newMonth, int newDay );
void Set( int newMonth );
int GetMonthNumber( ) const;
int GetDay( ) const;
bool IsValid ( ) const;
void Input( );
void Output( ) const;
private:
int m_month;
int m_day;
int m_isValid;
};
#endif
Here's how it might be used in a new class.
Note that a Vactation "has a" DayOfYear.
Also note the following about the code
- The Vacation constructor uses the initialization list
to call the DayOfYear constructor. If it did not do so,
then DayOfYear's default constructor would be automatically
be called.
- In this case, the Vacation class is a USER of the DayOfYear
class and is therefore limited to using DayOfYear's public methods.
- Note that in some cases, the methods of the Vacation class
simply call a method of the DayOfYear class. A classic example
of code reuse.
- Note the return type of Vacation::GetDayOfYear.
Returning by reference to const prevents copying of the
DayOfYear object and does not allow the user to change
Vacation's private data (m_startDOY) by using the reference.
More on this in class.
#ifndef VACATION_H
#define VACATION_H
#include "DayOfYear.h"
class Vacation
{
public:
Vacation( int startMonth, int startDay, int duration);
void Set( int newMonth, int newDay);
void Set( int newDuration);
const DayOfYear& GetDayOfYear( ) const;
int GetMonth( ) const;
int GetDay( ) const;
int GetDuration ( ) const;
bool IsValid ( ) const;
void Input ( );
void Output ( ) const;
private:
DayOfYear m_startDOY;
int m_nrDays;
bool m_isValid;
};
#endif
and here's the impmlementation of the Vacation class
from Vacation.cpp. The usual checking of pre-conditions is
removed so that we can focus on the aggregation aspects of
the code.
//-----------------------
// File: Vacation.cpp
// Author: D. Frey
// Date: 7/11/03
// Section: 1234
// Email: frey@cs.umbc.edu
// Project: none
//
// This code illustrates the implementation of
// a class which uses aggregation (aka composition)
//------------------------------------------------
#include
#include
#include "DayOfYear.h"
#include "Vacation.h"
using namespace std;
//----------------------
// Vacation constructor
// PreConditions
// none
// PostCondition
// a new object is created with the
// user specified values
//
//----------------------------
// Note the use of the initialization list
// to invoke the DayOfYear constructor for m_startDOY
//
// alternate implementations are also possible
// --- what might some of them be?
// --- why is this one best?
//
//----------------------
Vacation::Vacation( int month, int day, int nrDays)
: m_startDOY( month, day), m_nrDays( nrDays)
{
if (m_nrDays <= 0)
m_isValid = false;
else
m_isValid = m_startDOY.IsValid( );
}
//-----------------------------------
// Set( ) -- a mutator
// PreConditions:
// 1 <= month <= 12
// 1 <= day <= 31
// PostConditions
// month and day changed
//
// Note the use of DayOfYear's method
//-------------------------------------
void Vacation::Set (int newMonth, int newDay)
{
m_startDOY.Set( newMonth, newDay);
m_isValid = m_startDOY.IsValid( );
}
//-----------------------------------
// Set( ) -- a mutator
// PreConditions:
// 1 <= day
// PostConditions
// vacation duration changed
//
//-------------------------------------
void Vacation::Set (int newNrDays)
{
m_nrDays = newNrDays;
m_isValid = (m_nrDays > 0);
}
//-----------------------------------
// GetDayOfYear( ) -- an accessor
// PreConditions:
// none
// PostConditions
// returns the DayOfYear via reference to const
//
// Note the return type
//
//-------------------------------------
const DayOfYear& Vacation::GetDayOfYear ( ) const
{
return m_startDOY;
}
//-----------------------------------
// GetMonth( ) -- an accessor
// PreConditions:
// none
// PostConditions
// returns 1 = Jan, 2 = Feb, etc
//
// Note the use of DayOfYear's method
//-------------------------------------
int Vacation::GetMonth ( ) const
{
return m_startDOY.GetMonthNumber( );
}
//-----------------------------------
// GetDay( ) -- an accessor
// PreConditions:
// none
// PostConditions
// returns day of month that vacation begins
//
// Note the use of DayOfYear's method
//-------------------------------------
int Vacation::GetDay ( ) const
{
return m_startDOY.GetDay( );
}
//-----------------------------------
// GetDuration( ) -- an accessor
// PreConditions:
// none
// PostConditions
// returns vacation duration
//
//-------------------------------------
int Vacation::GetDuration ( ) const
{
return m_nrDays;
}
//-----------------------------------
// IsValid
// PreConditions:
// none
// PostConditions
// returns true if the Vacation data is valid
//
//-------------------------------------
int Vacation::GetDuration ( ) const
{
return m_isValid;
}
//--------------------------------
// Input
// Preconditions:
// none
// PostCondition:
// user prompted for month, day and duration
// which are saved in private data
//
// Note use of DayOfYear's method
//------------------------------------
void Vacation::Input ( )
{
m_startDOY.Input();
cout << "Enter number of days: ";
cin >> m_nrDays;
if (m_nrDays < 1)
{
cout << "Invalid vacation duration" << endl;
exit ( 1 );
}
}
//--------------------------------
// Output
// Preconditions:
// none
// PostCondition:
// month, day and duration displayed
//
// Note use of DayOfYear's method
//------------------------------------
void Vacation::Output ( ) const
{
m_startDOY.Output();
cout << "\nDuration: " << m_nrDays << endl;
}
Keeping perspective
When multiple classes are involved in a design as in this
case, it's important to keep the proper perspective when
implementing these classes.
As mentioned above, Vaction is a USER of the DayOfYear class
and is limited to using DayOfYear's public methods.
Vaction has no special privileges or permission which allow it
to access the private data members of the DayOfYear class.
It's also important to remember that the DayOfYear class
has no "knowledge" of how it's being used. There should be
no new methods or coded added to the DayOfYear class that
are in anyway specific to Vacation.
It's important that "boundaries" between classes (what each
class knows and doesn't know about the others) not be breached.
Last Modified: Monday, 28-Aug-2006 10:15:53 EDT