This article talks about Association, Aggregation and Composition Relationships between classes with some C++ examples. Background Association is a simple structural connection or channel between classes and is a relationship where all objects have their own lifecycle and there is no owner. Lets take an example of Department and Student. Multiple students can associate with a single Department and single student can associate with multiple Departments, but there is no ownership between the objects and both have their own lifecycle. Both can create and delete independently. Here is respective Model and Code for the above example. The code Code: #include<iostream.h> class Student; class Department { char* name_p; public: Department(char *dName) { cout<<"Department::ctor\n"; name_p = new char(sizeof(strlen(dName))); name_p = dName; } char* dName() const { return(name_p); } ~Department() { cout<<"Department::dtor\n"; delete(name_p); } }; class Student { char* name_p; public: Student(char *sName) { cout<<"Student::ctor\n"; name_p = new char(sizeof(strlen(sName))); name_p = sName; } char* sName()const { return(name_p); } ~Student() { cout<<"Student::dtor\n"; delete(name_p); }; }; class Course { Student * std_p; Department * dept_p; char * courseName_p; static unsigned int index; static Course *courseList[4]; public: Course(char* crseName, Student* student, Department* dept): courseName_p(0), std_p(student), dept_p(dept) { cout<<"Course:ctor\n"; if (index < 4) { courseName_p = new char(sizeof(strlen(crseName))); courseName_p = crseName; //insert this Course in courseList courseList[index] = this; ++index; } else { cout<<"Cannot accomodate any more Course\n"; } }; ~Course() { cout<<"Course:dtor\n"; delete (courseName_p); }; static char* findStudent(char *crseName, char* deptName) { for(int i=0; i<index; i++) { if ( (courseList[i]->getCourseName() == crseName) && (courseList[i]->getDeptName() == deptName) ) { return(courseList[i]->getStdName()); } } } char * getStdName()const {return(std_p->sName());}; char * getDeptName() const {return(dept_p->dName());}; char * getCourseName()const {return(courseName_p);}; }; unsigned int Course::index =0; Course* Course::courseList[4] = {0,0,0,0}; int main() { int i; cout<<"\nExample of Association class...\n"; cout<<"-----------------------------------\n\n"; cout<<"We have got 4 students ...\n"; Student *studentNames[4] = {new Student("Meera"), new Student("Moina"), new Student("Teena"), new Student("Mridula")} ; cout<<"\n"; cout<<"We have got 2 Departments...\n"; Department *departNames[2] = {new Department("Mathemetics"), new Department("ComputerSceince")} ; cout<<"\n"; cout<<"Here class Course Associates Student and Department, with a Course name ...\n"; Course course1("DataStructure",studentNames[0], departNames[1]); Course course2("Maths",studentNames[3], departNames[0]); Course course3("Geometry",studentNames[2], departNames[0]); Course course4("CA",studentNames[1], departNames[1]); cout<<"\n"; cout<<"Finding a Student using Course and Department...\n"; cout<<"Student who has taken Maths Course in Mathemetics Department is:"<<Course::findStudent("Maths", "Mathemetics")<<endl; cout<<"\n"; cout<<"Deletion of objects...\n\n"; for(i=0; i<4; ++i) { delete studentNames[i]; } cout<<"\n"; for(i=0; i<2; ++i) { delete departNames[i]; } cout<<"\n"; return(0); } output: ------ Example of Association class... ----------------------------------- We have got 4 students ... Student::ctor Student::ctor Student::ctor Student::ctor We have got 2 Departments... Department::ctor Department::ctor Here class Course Associates Student and Department, with a Course name ... Course:ctor Course:ctor Course:ctor Course:ctor Finding a Student using Course and Department... Student who has taken Maths Course in Mathemetics Department is:Mridula Deletion of objects... Student::dtor Student::dtor Student::dtor Student::dtor Department::dtor Department::dtor Course:dtor Course:dtor Course:dtor Course:dtor Aggregation is a specialize form of Association where all object have their own lifecycle but there is a ownership like parent and child. Child object can not belong to another parent object at the same time. We can think of it as "has-a" relationship. Implementation details: Typically we use pointer variables that point to an object that lives outside the scope of the aggregate class Can use reference values that point to an object that lives outside the scope of the aggregate class Not responsible for creating/destroying subclasses Lets take an example of Employee and Company. A single Employee can not belong to multiple Companies (legally!! ), but if we delete the Company, Employee object will not destroy. Here is respective Model and Code for the above example. The code Code: #include<iostream.h> class Employee { public: Employee(char *name){ cout<<"Employee::ctor\n"; myName_p = new char(sizeof(strlen(name))); myName_p = name; } char* disp(){return(myName_p);}; ~Employee() { cout<<"Employee:dtor\n\n"; delete (myName_p); } private: char *myName_p; }; class Company { public: Company(char * compName, Employee* emp){ cout<<"Company::ctor\n"; name = new char(sizeof(strlen(compName))); name = compName; myEmp_p = emp; }; ~Company() { cout<<"Company:dtor\n\n"; myEmp_p = NULL; }; private: char *name; Employee *myEmp_p; }; int main() { cout<<"\nExample of Aggregation Relationship \n"; cout<<"-----------------------------------------\n\n"; { cout<<"Here, an Employee-Keerti works for Company-MS \n"; Employee emp("Keerti"); { Company comp("MS", &emp); } // here Company object will be deleted, whereas Employee object is still there cout<<"At this point Company gets deleted...\n"; cout<<"\nBut Employee-"<<emp.disp(); cout<<" is still there\n"; } //here Employee object will be deleted return(0); } output: ------- Example of Aggregation Relationship ----------------------------------------- Here, an Employee-Keerti works for Company-MS Employee::ctor Company::ctor Company:dtor At this point Company gets deleted... But Employee-Keerti is still there Employee:dtor Composition is again specialize form ofAggregation. It is a strong type of Aggregation. Here the Parent and Child objects have coincident lifetimes. Child object dose not have it's own lifecycle and if parent object gets deleted, then all of it's child objects will also be deleted. Implentation details: 1. Typically we use normal member variables 2. Can use pointer values if the composition class automatically handles allocation/deallocation 3. Responsible for creation/destruction of subclasses Lets take an example of a relationship between House and it's Rooms. House can contain multiple rooms there is no independent life for room and any room can not belong to two different house. If we delete the house room will also be automatically deleted. Here is respective Model and Code for the above example. The code Code: #include<iostream.h> class House; class Room { public: Room() { }; static void createRoom_v(Room* (&room), House* hse, char* name) { room = new Room(hse, name); } Room(House* hse, char* myName) { cout<<"Room::ctor\n"; myHse_p = hse; if(NULL != myHse_p) { name_p = new char(sizeof(strlen(myName))); name_p = myName; } else { cout<<"Oops House itself is not Created Yet ...\n"; } }; ~Room() { cout<<"Room:dtor\n"; myHse_p = NULL; delete (name_p); }; void disp() { cout<< name_p; cout<<"\n"; } static void initList_v(Room *(& roomsList_p)[3]) { roomsList_p[3] = new Room[3]; } private: House * myHse_p; char * name_p; }; class House { public: House(char *myName) { cout<<"House::ctor\n"; name_p = new char(sizeof(strlen(myName)));; name_p = myName; Room::initList_v(roomsList_p); Room* myRoom; Room::createRoom_v(myRoom, this, "Kitchen"); roomsList_p[0] = myRoom; Room::createRoom_v(myRoom, this, "BedRoom"); roomsList_p[1] = myRoom; Room::createRoom_v(myRoom, this, "Drwaing Room"); roomsList_p[2] = myRoom; } ~House() { cout<<"House:dtor\n"; unsigned int i; cout<<"Delete all the Rooms ...\n"; for(i=0; i<3; ++i) { if(roomsList_p[i] != NULL) { delete (roomsList_p[i]); } } delete [] roomsList_p; delete (name_p); } void disp() { cout<<"\n\nName of the House :"<<name_p; if(roomsList_p != NULL) { unsigned int i; cout<<"\n\nRooms details...\n"; for(i=0; i<3; ++i) { if(NULL != roomsList_p[i]) { roomsList_p[i]->disp(); } } cout<<"\n\n"; } } private: char* name_p; Room* roomsList_p[3]; }; int main() { cout<<"\nExample of Composition Relationship\n"; cout<<"-----------------------------------------\n\n"; House hse("Vishranti Nilaya"); cout<<"\n\nHouse details...\n"; hse.disp(); cout<<"Here House itself creates the Rooms and Deletes as well, before it gets deletd...\n"; return(0); } output: ------- Example of Composition Relationship ----------------------------------------- House::ctor Room::ctor Room::ctor Room::ctor House details... Name of the House :Vishranti Nilaya Rooms details... Kitchen BedRoom Drwaing Room Here House itself creates the Rooms and Deletes as well, before it gets deletd... House:dtor Delete all the Rooms ... Room:dtor Room:dtor Room:dtor These relationships can also be explained with simple examples as below (these lines I have read in some blog): Association: Create a folder called "Links" Create a shortcut/link inside this folder and link it to www.go4expert.com Create another shortcut/link instide this folder and link it to www.google.com Ask your friend to do the same on another machine using same links (www.go4expert.com and www.google.com) Delete the "Links" folder, and open your browser to check if www.go4expert.com and www.google.com still exist or not Briefly, Association is a relationship where all the objects have different lifecycles. there is no owner. Aggregation: Create a file called file.txt Make a simple Application to open the File.txt (rw mode), but don't program it close the connection. Run an instance of this application (it should work ok and can open the file for rw) Keep the first instance, and run another instance of this application (In theory it should complain that it can't open the file in rw mode because it is already used by other application). Close the 2 instances (make sure you close the connection). From the above application, we understand that the Application and the File has a separate lifecycles, however this file can be opened only by one application simuletanously (there is only one parent at the same time, however, this parent can move the child to another parent or can make it orphan). Composition: Open a new Document name it as test.txt Write this sentence inside this document "This is a composition". Save the document. Now, delete this document. This is what is called composition, you can't move the sentence "This is a omposition" from the document because its lifecycle is linked to the parent (i.e. the document here !!)
Hi Mridula, Very good article and simple analogy as examples. But there is one minor mistake - The composition relationship should be graphically represented by "Solid Diamond" and not with "hollow Diamond" which is for Aggregation relationship. Sharanu , Amar
Hi Amar, Many thanks for your comment!! Yes. The solid diamond should have been there for composition. It is just missed. Some tools does not provide that. so. thanks, mridula.
Hi Mridula, The information provided here was really useful in understanding those concepts with no pain.. Thanks.. and may I know which tool you have used to create the class diagrams?
This is a quite good article. The point I did not understand was the ownership difference between Aggregation and Association. I find lot of different definitions of association and aggregation where aggregation is terms where no ownership exists. Also, in the above examples of association and aggregation I thought setting myEmp_p = NULL; is not appropriate. Is this to show ownership?