Composition vs Inheritance
IB Syllabus: B3.2.4 (HL) – Explain the role of composition and aggregation in class relationships.
HL Only – This page covers content assessed at HL level only.
Table of Contents
- Key Concepts
- Worked Examples
- Quick Check
- Trace Exercise
- Spot the Error
- Predict the Output
- Practice Exercises
- Connections
Key Concepts
Two Ways to Build Complex Objects
When designing a program with multiple classes, there are two fundamental approaches to relating them:
- Inheritance (is-a): A
DogIS-AAnimal. The child class extends the parent, inheriting its attributes and methods. - Composition (has-a): A
CarHAS-AEngine. One object contains another object as an attribute.
Both approaches allow code reuse, but they work in different ways and suit different situations.
Composition
Composition means a class contains objects of other classes as attributes. The contained objects cannot exist independently – they are created and destroyed with the container.
public class Engine {
private int horsepower;
public Engine(int horsepower) {
this.horsepower = horsepower;
}
public void start() {
System.out.println("Engine starting (" + horsepower + " HP)");
}
public int getHorsepower() { return horsepower; }
}
public class Car {
private String model;
private Engine engine; // Car HAS-A Engine
public Car(String model, int hp) {
this.model = model;
this.engine = new Engine(hp); // Engine created WITH the Car
}
public void drive() {
engine.start();
System.out.println(model + " is driving");
}
}
Car car = new Car("Tesla Model 3", 283);
car.drive();
Output:
Engine starting (283 HP)
Tesla Model 3 is driving
The Engine is created inside the Car constructor. When the Car is destroyed, its Engine is destroyed too. This is lifecycle dependency – the part cannot exist without the whole.
Composition = lifecycle dependency. The contained object is created by and belongs to the container. If the container is destroyed, the contained object is destroyed too. A car’s engine does not exist independently of the car.
Aggregation
Aggregation is a weaker form of has-a. The contained object can exist independently of the container.
public class Student {
private String name;
public Student(String name) {
this.name = name;
}
public String getName() { return name; }
}
public class Course {
private String title;
private Student[] students;
private int count;
public Course(String title, int maxSize) {
this.title = title;
this.students = new Student[maxSize];
this.count = 0;
}
public void enrol(Student s) {
if (count < students.length) {
students[count] = s;
count++;
}
}
public void printRoster() {
System.out.println(title + " roster:");
for (int i = 0; i < count; i++) {
System.out.println(" " + students[i].getName());
}
}
}
Student alice = new Student("Alice");
Student bob = new Student("Bob");
Course cs = new Course("Computer Science", 30);
cs.enrol(alice);
cs.enrol(bob);
Course maths = new Course("Mathematics", 30);
maths.enrol(alice); // Alice is in BOTH courses
cs.printRoster();
Output:
Computer Science roster:
Alice
Bob
The Student objects are created outside the Course and passed in. If the Course is deleted, the Students still exist. Alice is enrolled in two courses simultaneously – she does not “belong” to either one exclusively.
Aggregation = independent existence. The contained object is created elsewhere and shared. Deleting a playlist does not delete the songs. Removing a student from a course does not delete the student.
Composition vs Aggregation Summary
| Composition | Aggregation | |
|---|---|---|
| Relationship | Strong has-a | Weak has-a |
| Lifecycle | Part dies with whole | Part survives independently |
| Creation | Container creates the part | Part created externally, passed in |
| Sharing | Part belongs to one container | Part can be shared across containers |
| Example | Car has-a Engine | Course has-a Student |
| UML | Filled diamond | Empty diamond |
When to Use Inheritance vs Composition
Use inheritance when:
- There is a genuine is-a relationship (a
DogIS-AAnimal) - The child truly specialises the parent’s behaviour
- You want polymorphism (parent-type arrays holding child objects)
Use composition when:
- There is a has-a relationship (a
CarHAS-AEngine) - You want to combine capabilities from multiple sources
- The relationship could change at runtime (swap one engine for another)
- The contained object makes sense on its own
Common mistake: Using inheritance when composition is more appropriate. If you find yourself creating a child class just to reuse some methods, but the is-a relationship feels forced, composition is usually the better choice. “A
StackIS-AArrayList” is wrong – a Stack HAS-A list internally.
The “Favour Composition” Principle
A widely used design guideline says: favour composition over inheritance. This does not mean “never use inheritance.” It means:
- Inheritance creates tight coupling – changes to the parent can break all children
- Composition creates loose coupling – the container delegates to the contained object, which can be replaced
- Inheritance is fixed at compile time – you cannot change a Dog into a Cat
- Composition is flexible at runtime – you can swap an Engine for a different one
Use inheritance for genuine type hierarchies. Use composition for everything else.
Worked Examples
Example 1: Library System
A library has books, and each book has an author. Should Book extend Author? No – a book IS-NOT an author. A book HAS-A author.
public class Author {
private String name;
private String nationality;
public Author(String name, String nationality) {
this.name = name;
this.nationality = nationality;
}
public String getName() { return name; }
public String getNationality() { return nationality; }
}
public class Book {
private String title;
private Author author; // composition -- Book HAS-A Author
private int pages;
public Book(String title, Author author, int pages) {
this.title = title;
this.author = author;
this.pages = pages;
}
public String describe() {
return title + " by " + author.getName() +
" (" + author.getNationality() + "), " + pages + " pages";
}
}
Author orwell = new Author("George Orwell", "British");
Book b1 = new Book("1984", orwell, 328);
Book b2 = new Book("Animal Farm", orwell, 112);
System.out.println(b1.describe());
System.out.println(b2.describe());
Output:
1984 by George Orwell (British), 328 pages
Animal Farm by George Orwell (British), 112 pages
This is aggregation – the same Author object is shared between two books. Deleting a book does not delete the author.
Example 2: Combining Inheritance and Composition
A school management system needs Teacher and Student classes (both are Person) and a Department that contains teachers.
public class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() { return name; }
public int getAge() { return age; }
}
public class Teacher extends Person {
private String subject;
public Teacher(String name, int age, String subject) {
super(name, age);
this.subject = subject;
}
public String getSubject() { return subject; }
}
public class Department {
private String name;
private Teacher[] staff;
private int count;
public Department(String name, int maxStaff) {
this.name = name;
this.staff = new Teacher[maxStaff];
this.count = 0;
}
public void addTeacher(Teacher t) {
if (count < staff.length) {
staff[count] = t;
count++;
}
}
public void printStaff() {
System.out.println(name + " department:");
for (int i = 0; i < count; i++) {
System.out.println(" " + staff[i].getName() +
" (" + staff[i].getSubject() + ")");
}
}
}
Teacher t1 = new Teacher("Ms Smith", 35, "Computer Science");
Teacher t2 = new Teacher("Mr Jones", 42, "Mathematics");
Department cs = new Department("CS", 10);
cs.addTeacher(t1);
cs.printStaff();
Output:
CS department:
Ms Smith (Computer Science)
This example combines both patterns:
- Inheritance:
TeacherIS-APerson(extends) - Aggregation:
DepartmentHAS teachers (they exist independently)
Quick Check
Q1. What is composition in OOP?
Q2. What is the key difference between composition and aggregation?
Q3. A Hospital contains Room objects and Doctor objects. Rooms cannot exist without the hospital, but doctors can. Which is composition and which is aggregation?
Q4. What does "favour composition over inheritance" mean?
Q5. A student writes class Stack extends ArrayList. Why is this poor design?
Trace Exercise
Trace the object relationships. The Author is created once and shared between two Book objects.
Trace: Aggregation Relationships
Author a = new Author("Orwell", "British");
Book b1 = new Book("1984", a, 328);
Book b2 = new Book("Animal Farm", a, 112);| Variable | Type | describe() output |
|---|---|---|
| b1 | ||
| b2 |
Do b1 and b2 share the same Author object?
Both books reference the same Author object. This is aggregation – the Author exists independently and is shared.
Spot the Error
A student tries to model a school where each classroom has desks. They use inheritance instead of composition.
Bug Hunt: A school system where classrooms contain desks. The student used the wrong relationship type.
Pick the correct fix for line 1:
Predict the Output
Using the Car and Engine classes from Key Concepts. What does this print?
Car c = new Car("Honda Civic", 150);
c.drive();Type both lines separated by \n:
Practice Exercises
Core
- Classify relationships – For each pair, decide if the relationship is inheritance (is-a), composition (has-a, lifecycle dependent), or aggregation (has-a, independent):
- (a) University and Department
- (b) Dog and Animal
- (c) Playlist and Song
- (d) House and Room
- (e) Manager and Employee
- Build with composition – Create a
Phoneclass that contains aBatteryobject (withcapacityandcharge()method) and aScreenobject (withsizeanddisplay(String message)method). The Phone should have ause()method that charges the battery and displays a message.
Extension
-
Refactor inheritance to composition – A student wrote
class ElectricCar extends Battery. Explain why this is wrong and rewrite it using composition so thatElectricCarextendsCarand HAS-ABattery. -
Explain the trade-offs – A game has
Warrior,Mage, andArcherclasses. Each has aWeaponthat can be swapped during gameplay. ShouldWeaponbe a parent class (inheritance) or an attribute (composition)? Justify your answer with at least two reasons.
Challenge
- Restaurant system – Design a restaurant ordering system with composition and aggregation. A
RestaurantHASMenuitems (composition – menu belongs to the restaurant). AnOrderHASMenuItemreferences (aggregation – the same menu item can appear in multiple orders). Create all classes with appropriate constructors. Write a methodcalculateTotal()that returns the total price of an order.
Connections
- Prerequisites: Aggregation – has-a relationships with SL-level examples
- Prerequisites: Inheritance – is-a relationships using extends
- Prerequisites: Abstract Classes and Interfaces – alternatives to inheritance
- Related: Encapsulation – composition enforces encapsulation by hiding internal objects
- Related: UML Class Diagrams – composition shown with filled diamond, aggregation with empty diamond