Object-oriented programming (OOP) is a cornerstone of robust and maintainable Java applications. Two crucial OOP concepts, composition and aggregation, often cause confusion. Both represent “has-a” relationships between objects but differ significantly in their implications and usage. This article clarifies these differences through practical examples.
Table of Contents
- Composition in Java
- Aggregation in Java
- Composition vs. Aggregation
- Working with Composition in Java
- Working with Aggregation in Java
Composition in Java
Composition, a “strong” has-a relationship, signifies a whole-part relationship where the part’s lifecycle is entirely dependent on the whole. Destruction of the whole also destroys its parts; the parts cannot exist independently. Consider a car: it’s composed of an engine, wheels, doors, etc. Scrapping the car disposes of these components.
In Java, composition is typically implemented by directly instantiating part objects within the whole object’s class. The whole object creates and manages these parts.
Aggregation in Java
Aggregation, a “weaker” has-a relationship, indicates that the part can exist independently of the whole. The whole object holds references to the parts, but their lifecycles are not directly linked. For instance, a university “has-a” department, but the department persists even if the university closes.
In Java, aggregation involves the whole object holding references to part objects. These references can be managed separately. Parts can be created before or after the whole object and survive its destruction.
Composition vs. Aggregation
The table below summarizes the key differences:
Feature | Composition | Aggregation |
---|---|---|
Relationship | Strong “has-a” (whole-part) | Weak “has-a” |
Lifecycle | Part’s lifecycle depends on the whole’s | Part’s lifecycle independent of the whole’s |
Ownership | Whole object owns and manages the parts | Whole object holds references to the parts |
Part’s Existence | Part cannot exist independently | Part can exist independently |
Implementation | Direct instantiation within the whole object | References to independent part objects |
Working with Composition in Java
Let’s illustrate composition with a Car
composed of an Engine
:
class Engine {
public void start() { System.out.println("Engine started"); }
}
class Car {
private Engine engine;
public Car() {
this.engine = new Engine();
}
public void drive() {
engine.start();
System.out.println("Car driving");
}
}
public class Main {
public static void main(String[] args) {
Car myCar = new Car();
myCar.drive();
}
}
The Engine
object is created within the Car
class. The Engine
‘s existence depends on the Car
.
Working with Aggregation in Java
Now, let’s illustrate aggregation with a university and its departments:
class Department {
private String name;
public Department(String name) { this.name = name; }
public String getName() { return name; }
}
class University {
private Department[] departments;
public University(Department[] departments) {
this.departments = departments;
}
public void printDepartments() {
for (Department dept : departments) {
System.out.println("Department: " + dept.getName());
}
}
}
public class Main {
public static void main(String[] args) {
Department cs = new Department("Computer Science");
Department math = new Department("Mathematics");
Department[] deps = {cs, math};
University uni = new University(deps);
uni.printDepartments();
}
}
The University
holds references to Department
objects. The Department
objects exist independently and can be used elsewhere.
Understanding composition and aggregation allows for designing more robust, maintainable, and well-structured Java applications. The choice depends on the design requirements and desired lifecycle management of objects.