Overview of This Lesson

Aggregation and Composition are class relationships that define classes with a "part-whole" relationsihp: where one class is part of another, or one class is contained inside another. It's a very common relationship but it does mean that there is a strong dependency between the two classes. This lesson looks at aggregation and composition in more detail, including the important differences between the two.

Pre-Requisites

It is assumed that you've gone over the Introduction to Class Relationships lesson and are already somewhat familiar with Dependency and Association.

Aggregation

Aggregation and Composition are types of Association, and are stronger forms of dependency than Association. The difference between Aggregation and Composition has to do with the life cycle of the "part" object in relation to the "container" or "whole" object. It makes the most sense to talk about Aggregation, first.

Aggregation is a stronger form of Association in that it involves ownership: ClassA (the "whole" or "container") contains or owns ClassB (the "part"). The containing class (ClassA) "owns" the part class (ClassB). ClassB is part of the state of ClassA, just like in association. The main difference is that the aggregation is not bi-directional: it's uni-directional. In other words, the "action" goes in only one direction.

For example, in a role playing game you might have a Player and a Weapon class:

Player
- name : String
- playerClass : String
- weapons : Weapon[]
+ armPlayer(weapon : Weapon) : void
public class Player {
private String name;
private String playerClass;
private Weapon[] weapons;
public void armPlayer(Weapon w) { ... }   
}
Weapon
- name : String
- damage : int
public class Weapon {
private String name;
private int damage;
}

In the above example, a Player can have some weapons (i.e. a Player could own a broad sword, a dagger, and a crossbow), and can arm themselves with one of those weapons and fight with it. The Player class contains a list of Weapon objects that player owns.

A Weapon can belong to a Player, and can even change ownership and belong to a different Player. However, a Weapon can only belong to one player at a time (or no player, if the weapon is discarded). There is clear ownership here: The Weapon belongs to a Player, when it is owned by someone.

What makes this a uni-directional Aggregation relationship is that the Player class "knows about" (has a reference to) the Weapon but the Weapon doesn't "know about" the Player class (there is no Player data member in the Weapon class). If this were an association relationship, the Weapon class would contain a reference to the Player that owns it.

A Weapon can exist without any Player as owner (e.g. if the Weapon is dropped or discarded by a Player) and a Player can exist without owning any weapon at all. This is also an important characteristic of Aggregation: The part can exist without the whole/container and vice versa.

In an Aggregation relationship, the two objects can exist independent of each other and have their own life cycles. There is ownership, but removing the part doesn't cause a removal of the whole, and vice versa. For example, deleting a Player doesn't cause a deletion of the Weapon, and vice versa. Additionally,the parts can be parts of more than one thing: they're not exclusive. For example, a Weapon can be owned by different Players (Players could share a weapon, they just couldn't use it at the same time).

Here is another common example: a Car class and an Engine class are two completely different classes, but a Car has an Engine. Both Car and Engine have their own attributes and methods, but Car might include an attribute for the Engine object -- a Car owns an Engine. That Engine could be taken out of that Car and put into another Car, or the Engine could be taken out of the Car and put into inventory to wait for another Car that might need that Engine. Also, the Car owns the Engine, but the Engine doesn't own the Car.

Car
- model : String
- year : int
engine : Engine
currentGasQty : double
+ Car()
+ Car(model : String, year : int, engine : Engine, qty : int)
+ start() : void
+ turn(direction : direction) : void
+ accelerate(speed : double) : void
Engine
- type : String
- cylinders : int
+ Engine()
+ Engine(type : String, cylinders : int)

Notice that the context in the above example was inventory: Perhaps parts inventory in an auto shop. If the context were different, then the class relationship between Car and Engine might be different. For example, if this were a racing game where the car needs the Engine in order to accelerate and decelerate, this would be a stronger form of Aggregation called Composition.

Composition

Composition is a stronger form of Aggregation. In a Composition relationship, the part is exclusive to the container - it can't belong to more than one container or whole. For example, a House object can contain many Room objects, but a Room object can only belong to one House.

Additionally, with composition the part can't exist without the whole, and the life cycle of the part is completely dependent upon the life cycle of the whole. When the House is created, the Room is created with it. When the House is destroyed, the Room is destroyed with it (you can't pick up a Room and put it into another House).

Another example might be Student and Name: A Student owns or contains Name. A Name can't exist on its own: you can't have a Name in your database without belonging to a Student. If you delete a Student, the Name is also deleted. A Name can only belong to one Student (Students that happen to have the exact same Name still don't share the same Name object).

Name
- firstName : String
- middleName : String
- lastName : String
+ Name(first : String, middle : String, last : String)
+ getFirstName() : String
+ getMiddleName() : String
+ getLastName() : String
public class Name {
private String firstName;
private String middleName;
private String lastName;
public Name(String first, String middle, String last) { ... }
public String getFirstName() { ... }
public String getMiddleName() { ... }
public STring getLastName() { ... }
}
Student
- name : Name
- address : Address
- phoneNumber : String
+ Student()
+ setName(first : String, middle : String, last : String) : void
+ getName() : String
public class Student {
private Name name;
private Address address;
private String phoneNumber;
public Student() { ... }
public void setName(String first, String middle, String last) {
    name = new Name(first, middle, last);
}
public String getName() {
    return name.getLastName() + ", " + name.getFirstName()
         + " " + name.getMiddleName();
}
}

In the example above, a Student owns a Name object. Notice that the getName() method doesn't actually return a Name object and setName() doesn't accept a Name object as an argument. The Name object is completely encapsulated inside the Student class: in a true composition relationship, there should be no access to the part object outside of the container class. In this example, there is no access to the Student's name object outside of the Student class.

This shows the strong dependency of the part's life cycle on the container's life cycle: The Name object is only created when the Student object is created. When the Student object is destroyed, the Name object that belongs to that Student is also destroyed, as there are no other references to it outside of the Student class.

Aggregation and Composition in UML

Aggregation is shown with a solid line and a hollow diamond at the end of the line that connects the "whole" or "container" part of the relationship. Composition is similar, but with a solid diamond. Example:

Aggregation and Composition relationships
               shown in an object diagram.
Aggregation and Composition relationships shown in an object diagram.

Exercise

Imagine an application for a ride-sharing application that has a Map, Drivers, Customers, Trips, Origins, and Destinations. Where would there be aggregation or composition between classes in the application? Describe each instance of aggregation and composition.