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
  1. 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.
  2. In this case, the Vacation class is a USER of the DayOfYear class and is therefore limited to using DayOfYear's public methods.
  3. 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.
  4. 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 <iostream> #include <cstdlib> #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