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

  1. Key Concepts
    1. Two Ways to Build Complex Objects
    2. Composition
    3. Aggregation
    4. Composition vs Aggregation Summary
    5. When to Use Inheritance vs Composition
    6. The “Favour Composition” Principle
  2. Worked Examples
    1. Example 1: Library System
    2. Example 2: Combining Inheritance and Composition
  3. Quick Check
  4. Trace Exercise
  5. Spot the Error
  6. Predict the Output
  7. Practice Exercises
    1. Core
    2. Extension
    3. Challenge
  8. 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 Dog IS-A Animal. The child class extends the parent, inheriting its attributes and methods.
  • Composition (has-a): A Car HAS-A Engine. 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 Dog IS-A Animal)
  • 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 Car HAS-A Engine)
  • 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 Stack IS-A ArrayList” 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: Teacher IS-A Person (extends)
  • Aggregation: Department HAS 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);
VariableTypedescribe() 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.

1public class Classroom extends Desk { 2 private String roomNumber; 3 4 public Classroom(String roomNumber) { 5 super("wood", 75); 6 this.roomNumber = roomNumber; 7 } 8}

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

  1. 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
  2. Build with composition – Create a Phone class that contains a Battery object (with capacity and charge() method) and a Screen object (with size and display(String message) method). The Phone should have a use() method that charges the battery and displays a message.

Extension

  1. Refactor inheritance to composition – A student wrote class ElectricCar extends Battery. Explain why this is wrong and rewrite it using composition so that ElectricCar extends Car and HAS-A Battery.

  2. Explain the trade-offs – A game has Warrior, Mage, and Archer classes. Each has a Weapon that can be swapped during gameplay. Should Weapon be a parent class (inheritance) or an attribute (composition)? Justify your answer with at least two reasons.

Challenge

  1. Restaurant system – Design a restaurant ordering system with composition and aggregation. A Restaurant HAS Menu items (composition – menu belongs to the restaurant). An Order HAS MenuItem references (aggregation – the same menu item can appear in multiple orders). Create all classes with appropriate constructors. Write a method calculateTotal() 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

© EduCS.me — A resource hub for IB Computer Science

This site uses Just the Docs, a documentation theme for Jekyll.