Relationships in OOAD
In Object-Oriented design, classes don’t exist in isolation. They interact with each other. We categorize these interactions into several types of relationships.
1. Association
Association is the most general relationship. It represents a “has-a” or “uses-a” relationship where objects have their own independent lifecycle and there is no ownership.
Python Example of Association
class Teacher:
def __init__(self, name):
self.name = name
class Student:
def __init__(self, name):
self.name = name
# A teacher is associated with a student, but they exist independently.
t = Teacher("Mr. Smith")
s = Student("Alice")
2. Aggregation (Weak Has-A)
Aggregation is a special form of association. It represents a “part-of” relationship where the part can exist independently of the whole. The parent “aggregates” the child.
Lifecycle: If the parent is destroyed, the child usually survives.
Java Example of Aggregation
class Department {
private List<Employee> employees;
// If the Department is closed, the employees still exist.
}
3. Composition (Strong Has-A)
Composition is a strong form of aggregation. It represents a “part-of” relationship where the part cannot exist independently of the whole.
Lifecycle: If the parent is destroyed, the child is also destroyed.
C++ Example of Composition
class Heart {
public:
void beat() {}
};
class Human {
Heart heart; // The heart is part of the human.
public:
// If Human is destroyed, heart is destroyed too.
};
4. Multiplicity
In UML and OOAD, we also define how many instances are involved:
- 1..1: Exactly one
- 0..*: Zero or more
- 1..*: One or more
Comparing the Three
| Feature | Association | Aggregation | Composition |
|---|---|---|---|
| Relationship | Link between two classes | Weak “Has-A” | Strong “Has-A” |
| Lifecycle | Independent | Independent | Dependent |
| Example | Teacher & Student | Department & Teacher | House & Room |
| UML Symbol | Straight Line | Hollow Diamond | Filled Diamond |
Why it Matters
Choosing the right relationship type dictates:
- How memory is managed (especially in languages like C++).
- How objects are initialized and cleaned up.
- The level of coupling between components.
In Design Patterns, we often prefer Composition over Inheritance to allow for more flexible code reuse without the rigid hierarchies that inheritance creates.