In the real world, objects interact with each other in many meaningful ways. A car has an engine, a teacher teaches students, and a library contains books. These real-life associations form the basis for how we design relationships between classes in Object-Oriented Programming (OOP).
Understanding and modeling these relationships properly helps create modular, scalable, and reusable software systems. Let’s explore the main types of class relationships in OOP:
Relationships between classes can be categorized into three major types:
- Association: A general relationship where one class interacts with another.
- Aggregation: A specialized form of association that represents a “has-a” relationship with a weaker bond.
- Composition: A more restrictive form of aggregation where the lifecycle of the related objects is tightly coupled.
Association
Association represents a general connection between two classes. It implies that objects of one class use or are connected to objects of another class. However, neither class owns the other.
Example:
class Teacher {
public:
void teach(Student s);
};
class Student {
public:
void study();
};
Here, Teacher
and Student
are associated. A teacher teaches student, but neither owns the other permanently.
The connection can be one-to-one, one-to-many, or many-to-many, enabling different levels of collaboration and data sharing between the classes. Understanding this fundamental concept is key to designing systems that mirror real-world interactions.
Types of Association:
- One-to-One: One instance of a class is associated with exactly one instance of another class. For example, a Person class might have a one-to-one relationship with a Passport class.
- One-to-Many: One instance of a class is associated with multiple instances of another class. For instance, a Teacher class may be associated with multiple Student objects.
- Many-to-Many: Many instances of a class are associated with many instances of another class. For example, a Student class might be associated with multiple Course objects, and each Course object can have multiple Students.
In most programming languages, association is implemented by referencing one class in another using pointers, references, or collections.
Aggregation (Has-a)
Aggregation is a specialized form of association. It represents a “whole-part” relationship where the “whole” and “part” can exist independently. For example, a Department class may contain multiple Employee objects, but the Employee can exist independently of the department. Consider the following snippet:
#include <bits/stdc++.h>
using namespace std;
// Employee Class
class Employee {
private:
string name;
public:
Employee(string name) {
this->name = name;
}
};
// Department Class
class Department {
private:
vector<Employee> employees;
public:
Department(vector<Employee> employees) {
this->employees = employees;
}
};
In this example, Employee objects are aggregated into a Department object. Even if the Department object is deleted, the Employee objects can continue to exist.
Characteristics of Aggregation:
- Independence: The lifecycle of the "part" is not dependent on the “whole.”
- Weaker Bond: The relationship is less tightly coupled compared to composition.
Composition (Owns-a)
Composition is a stricter form of aggregation where the "whole" and "part" are tightly coupled. If the "whole" is destroyed, the "parts" are also destroyed. This represents a "part-of" relationship. For example, a House class might contain multiple Room objects. If the House is destroyed, the Room objects cease to exist.
Consider the given code snippet:
#include <bits/stdc++.h>
using namespace std;
class Room {
private:
string name;
public:
Room(string name) {
this->name = name;
}
};
class House {
private:
vector<Room> rooms;
public:
House() {
rooms.push_back(Room("Living Room"));
rooms.push_back(Room("Bedroom"));
}
};
Here, Room objects are part of the House object. If the House is destroyed, the Room objects are also destroyed.
Characteristics of Composition
- Dependency: The lifecycle of the "part" is entirely dependent on the “whole.”
- Stronger Bond: The relationship is tightly coupled.
Inheritance (Is-a)
Inheritance represents an “is-a” relationship. It models specialization, where a subclass inherits properties and behaviors from a super class.
class Animal {
public:
void eat();
};
class Dog : public Animal {
public:
void bark();
};
A Dog is-a Animal. The Dog inherits the eat behavior from Animal.
Dependency (Uses-a)
Dependency represents a "uses-a" relationship, usually when a class depends on another class temporarily to perform some function. This is often seen when objects are passed as parameters.
Example:
class Printer {
public:
void print(Document doc);
};
Here, Printer depends on Document. If the Document class changes, the Printer class may also need to change. However, it’s a loose, temporary connection.
Summary Table
Relationship | Description | Lifetime Dependency | Example |
---|---|---|---|
Association | General connection | No | Teacher - Student |
Aggregation | Has-a (weak) | No | Car - Engine |
Composition | Has-a (strong) | Yes | Bicycle - Wheel |
Inheritance | Is-a | Yes (permanent) | Dog - Animal |
Dependency | Uses-a (temporary) | No | Printer - Document |