ReviseAlgo Logo

Class Relationships

Aggregation

Has-a relationship (weak)

Last Updated: June 26, 2026 20 min read

Aggregation is a specialized form of Association that models a "has-a" or "whole-part" relationship between classes. It represents a weak ownership structure where the container (the "whole") holds references to the contained entities (the "parts"), but the parts exist independently and can survive the destruction of the whole.

1. Learning Objectives

  • Identify Aggregation relationships as weak "whole-part" models.
  • Differentiate Aggregation from Association and Composition.
  • Apply Dependency Injection to pass part instances into whole objects.
  • Trace stack references and heap variables during container deletion to verify part survival.
  • Implement encapsulation rules for aggregated lists in Java, Python, and C++.

2. Problem Statement

When modeling real-world entities, some objects naturally belong to a larger container. For example, a Department contains Employees. If we implement this by instantiating the Employee objects inside the Department's constructor, we bind their lifecycles together.

This creates a major design problem: if a department is shut down, the employees shouldn't be deleted. They are independent entities who can be transferred to another department or continue working. We need a way to model this whole-part relationship where the container manages the grouping, but the child elements exist independently.

3. Real-world Analogy

Think of a Library and Books:

  • The Library is the container (the "whole"), and the Books are the items inside (the "parts").
  • The Library "has-a" collection of Books.
  • If the Library closes down permanently, the books are not burned or destroyed. They are simply moved to other libraries, donated, or sold.

Because the books survive the closure of the library, the relationship is a weak whole-part association, or Aggregation.

4. Theory

Aggregation is an asymmetric whole-part relationship. In UML diagrams, it is represented by a hollow diamond pointing toward the container (the whole).

Core Characteristics:

  • Whole-Part Structure: Unlike general association (which is a peer-to-peer relationship), aggregation establishes a clear container-contained hierarchy.
  • Weak Ownership: The whole "owns" the parts in terms of containing them, but it does not control their creation or destruction.
  • Separate Lifecycles: Parts are created outside the whole and passed in. If the whole is destroyed, the parts survive.
  • Shareability: A part can theoretically belong to multiple containers at once (e.g., an Employee can work for two departments, or a Song can belong to multiple Playlists).

5. Visual Diagrams (UML & Memory structures)

Class Diagram

The hollow diamond denotes Aggregation. It is placed on the side of the container (Department).

Department
- name: String
- employees: List<Employee>
+ addEmployee(Employee)
1               *
Hollow Diamond (Aggregate)
Employee
- id: String
- name: String
+ getName(): String

Object Diagram

Shows specific instances where two independent employee objects are nested conceptually under the engineering department:

engineering: Department
name = "Engineering"
aggregates
emp1: Employee
name = "Alice"
emp2: Employee
name = "Bob"

Memory Diagram (Verifying Survival)

If we set the engineering stack reference to null, the department object is dereferenced and marked for GC, but the Employee objects remain on the heap because the stack variables alice and bob still reference them.

Stack Frame

engineering = @0xFA12
alice = @0xBE34
points to

Heap space

@0xFA12 (Department): [Eligible for GC]
name: "Engineering"
employees: [@0xBE34]
@0xBE34 (Employee): [Kept alive]
name: "Alice"

Object Lifecycle

1. External Construction (Employees instantiated in outer scope)
2. Injection (Passed to Department constructor or adder method)
3. Operation (Department lists employees, employee states are read)
4. Department Deletion (Department set to null; Employee heap structures remain untouched)

6. Syntax Explanation

In Aggregation, child objects are injected into the parent container. We do not use the new keyword to create child objects inside the parent's constructor.

  • Java: Pass objects into constructors or methods, e.g., public Department(String name, List<Employee> employees) or public void addEmployee(Employee emp).
  • Python: Pass objects to the initializer: def __init__(self, name, employees=None):.
  • C++: Use std::shared_ptr<Employee> to manage the shared reference. When the Department object goes out of scope and is destroyed, its vector of shared pointers is cleared. However, the Employee instances remain allocated on the heap as long as another shared pointer in the outer scope keeps them alive.

7. Step-by-Step Implementation

  • Step 1: Create the Employee class with private fields (name, id) and standard getter methods.
  • Step 2: Create the Department class containing a private list of Employees.
  • Step 3: Implement constructor and method injection: pass existing Employee instances into the department's constructor or addEmployee() method.
  • Step 4: Write a verification script: instantiate a Department and two Employees, register them, set the Department to null, and print the Employees to prove they survive.

8. Complete Code (Mini Project)

9. Code Walkthrough

The core of Aggregation is dependency injection:

  • In the Main client code, we instantiate alice and bob first. They are declared as local variables in the main thread's stack frame.
  • We pass these instances to the department using engineering.addEmployee(alice). The department's list appends the reference pointer to the existing heap structures.
  • When we set engineering = null, the JVM GC marks the Department instance for reclamation. It clears the references inside the department's internal array list, but the original Employee heap structures are preserved because they are still referenced by the stack variables alice and bob.
  • In C++, the department is defined inside a scope block { ... }. When execution leaves this block, the destructor of Department runs and releases its shared pointers. The reference count of the Employee objects decrements by 1, but remains at 1 because alice and bob are still holding active shared_ptrs in the main block scope. Thus, no memory leak or premature deletion occurs.

10. Execution Flow

  1. Step 1: Create Employee objects on the heap; store references in stack frames.
  2. Step 2: Create Department object on the heap; store reference in stack frame.
  3. Step 3: Call addEmployee, passing the references. The department's internal array list points to the same Employee objects.
  4. Step 4: Overwrite the Department reference on the stack with null (or let it go out of scope).
  5. Step 5: The JVM collects the Department object. The Employee heap allocations remain intact, accessible via the original stack variables.

11. Internal Working

At runtime, the JVM separates stack frames (tracking active code execution) from heap spaces (storing object data structures).

When new Employee() runs, space is reserved on the heap. A stack slot holds the address (e.g., @0xBE34).

When a Department aggregates this employee, its internal list is updated to store the address @0xBE34. When the department is garbage collected, the incoming reference count to the employee from the department's array list drops to zero. However, the stack frame still holds the reference address @0xBE34. Therefore, the employee object is not reclaimed.

12. Complexity Analysis

  • Time Complexity:
    • Adding a part to a collection: $O(N)$ to verify no duplicates are present (or $O(1)$ if a hash set is used).
    • Removal: $O(N)$ to search and clear the reference pointer.
    • Lifecycle clearing (parent deletion): $O(K)$ where $K$ is the number of aggregated elements, since clearing the parent releases $K$ shared references.
  • Space Complexity: $O(1)$ extra space per association, as we only store reference addresses.

13. Best Practices

  • Use dependency injection: Always pass pre-created parts into the aggregate class rather than instantiating them inside.
  • Encapsulate collections: Expose getter methods that return read-only collection wrappers or shallow copies to prevent callers from directly mutating internal list structures.
  • Clear references cleanly: If circular links exist, explicitly clear collections or use weak pointers to avoid retention leaks.

14. Common Mistakes

  • Instantiating internally: Creating the employee object using new Employee() inside the Department constructor. This makes it a Composition relationship instead of Aggregation, and the employee cannot exist without the department.
  • Cascading deletes: Deleting all Employee entries from the database when a Department is deleted. In Aggregation, the parts exist independently, so cascading deletes should not be configured.
  • Memory Retention Leaks: Forgetting that a parent object is still referencing parts, which prevents the parts from being garbage collected even after they are no longer needed.

15. Interview Questions

Q: How do you verify if a relationship is Aggregation or Composition in an interview?
Answer: Ask: "If the parent container is deleted, do the child parts still have meaning and survive in the system?" If yes, it is Aggregation (e.g., Library and Books). If no, it is Composition (e.g., House and Rooms).
Q: How do you code Aggregation in Java to ensure weak ownership?
Answer: Inject pre-constructed instances into the constructor or setter methods. Do not call the constructor of the child objects inside the parent class.
Q: What is the main structural difference between Association and Aggregation?
Answer: Association represents a peer-to-peer relationship with no ownership (e.g., Doctor and Patient). Aggregation is a whole-part relationship where one class acts as a container for others, but without strict lifecycle binding.

16. Practice Exercises

  • Easy: Model a Playlist and Song class where a playlist references songs, and songs can belong to multiple playlists.
  • Medium: Design a SportsTeam and Player model. Expose a method tradePlayer(Player p, SportsTeam target) that transfers a player reference from one team to another without destroying the player object.
  • Hard: Build an e-commerce ShoppingCart that references ProductItems. ProductItems are stored in a global catalog. Ensure that modifications to the Cart's quantity do not modify the state of the shared ProductItem in the catalog.

17. Challenge Problem

Design a university management module where a Professor holds associations with multiple Departments and teaches multiple Courses. Implement a clean-up method dissolveDepartment(Department d) that closes a department, automatically re-assigns its professors to a default general department, and preserves all their ongoing courses.

18. Summary

  • Aggregation is a weak "has-a" relationship representing a whole-part hierarchy.
  • The lifecycle of aggregated parts is independent of the whole container.
  • Objects are injected into the container rather than instantiated inside it.
  • In UML, Aggregation is represented by a hollow diamond pointing to the container.

19. Cheat Sheet

Concept UML Notation Lifecycle Ownership Creation Location
Association Solid line None (Peers) Independently in client scope
Aggregation Hollow diamond pointing to whole Weak ownership (survives whole) Created externally, injected inside
Composition Filled diamond pointing to whole Strong ownership (dies with whole) Instantiated directly inside whole

20. Quiz

1. What type of relationship is Aggregation?

A) Strong ownership whole-part
B) Weak ownership whole-part (Correct)
C) Peer-to-peer with zero hierarchy

2. Which UML symbol represents Aggregation?

A) Solid diamond
B) Hollow diamond (Correct)
C) Plain solid line with arrowhead

3. In Aggregation, how do child parts receive their initial reference values?

A) They are instantiated using the 'new' keyword inside the container constructor
B) They are passed into constructors or setters from an external scope (Correct)
C) They are dynamically cast from parent classes

4. If a Library (container) is garbage-collected, what happens to its aggregated Books (parts)?

A) They are instantly garbage-collected
B) They remain allocated in memory as long as they are referenced elsewhere (Correct)
C) They throw an OrphanedObjectException

5. Why are list collections cloned or returned as unmodifiable views in getter methods?

A) To speed up heap lookup iterations
B) To protect encapsulation and prevent direct modification of the relationship list (Correct)
C) To automatically free unused elements

6. What happens if you define this.employee = new Employee() inside the Department constructor?

A) The relationship is elevated to Composition (Correct)
B) The relationship remains Aggregation
C) The code throws a compilation error

7. What is a key indicator that a relationship is Aggregation rather than Association?

A) The classes are related but don't represent a whole-part container structure
B) One class conceptually acts as a container or whole for the other (Correct)
C) The classes are completely static

8. Which smart pointer setup represents Aggregation in C++ where parts are shareable?

A) std::unique_ptr
B) std::shared_ptr (Correct)
C) std::weak_ptr

9. What is a common mistake when database schemas are configured for Aggregation?

A) Using foreign keys
B) Setting cascade deletes on container deletion (Correct)
C) Setting tables as read-only

10. In Java, what prevents an aggregated part from being garbage collected when its parent is deleted?

A) An active reference on the call stack or in another heap object (Correct)
B) The finalize() method block
C) A static reference import

21. Next Lesson Preview

In the next lesson, we will explore Composition to see how strong ownership binds the lifecycles of parts directly to the whole, ensuring they are created and destroyed together!