Class Relationships
Aggregation
Has-a relationship (weak)
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
Employeecan work for two departments, or aSongcan belong to multiplePlaylists).
5. Visual Diagrams (UML & Memory structures)
Class Diagram
The hollow diamond denotes Aggregation. It is placed on the side of the container (Department).
Object Diagram
Shows specific instances where two independent employee objects are nested conceptually under the engineering department:
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
Heap space
name: "Engineering"
employees: [@0xBE34]
name: "Alice"
Object Lifecycle
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)orpublic 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
Employeeclass with private fields (name, id) and standard getter methods. - Step 2: Create the
Departmentclass containing a private list ofEmployees. - Step 3: Implement constructor and method injection: pass existing
Employeeinstances into the department's constructor oraddEmployee()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
Mainclient code, we instantiatealiceandbobfirst. 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 variablesaliceandbob. - In C++, the department is defined inside a scope block
{ ... }. When execution leaves this block, the destructor ofDepartmentruns and releases its shared pointers. The reference count of the Employee objects decrements by 1, but remains at 1 becausealiceandbobare still holding activeshared_ptrs in themainblock scope. Thus, no memory leak or premature deletion occurs.
10. Execution Flow
- Step 1: Create
Employeeobjects on the heap; store references in stack frames. - Step 2: Create
Departmentobject on the heap; store reference in stack frame. - Step 3: Call
addEmployee, passing the references. The department's internal array list points to the sameEmployeeobjects. - Step 4: Overwrite the
Departmentreference on the stack withnull(or let it go out of scope). - 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 theDepartmentconstructor. 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
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).
Answer: Inject pre-constructed instances into the constructor or setter methods. Do not call the constructor of the child objects inside the parent class.
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
PlaylistandSongclass where a playlist references songs, and songs can belong to multiple playlists. - Medium: Design a
SportsTeamandPlayermodel. Expose a methodtradePlayer(Player p, SportsTeam target)that transfers a player reference from one team to another without destroying the player object. - Hard: Build an e-commerce
ShoppingCartthat referencesProductItems. 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!