Intermediate 25 min read

Composition Over Inheritance

By the end of this lesson, you'll be able to:

  • Define composition: building a class out of other objects rather than extending a parent
  • Recognize when a "has-a" relationship is a better fit than "is-a"
  • Refactor an awkward inheritance hierarchy into composition

Prerequisites: "Polymorphism"

When inheritance stops fitting

// Awkward: does a DeliveryVan really "extend" an Engine?
public class Engine {
    public void start() { System.debug('Engine running.'); }
}

public class DeliveryVan extends Engine { // this reads wrong
    public Decimal cargoCapacityKg;
}

A DeliveryVan has an engine — it isn't a kind of engine. Forcing this into extends fails the "is-a" test from the Inheritance lesson, even though it compiles.

Composition: has-a instead of is-a

public class Engine {
    public void start() {
        System.debug('Engine running.');
    }
}

public class DeliveryVan {
    private Engine engine = new Engine();
    public Decimal cargoCapacityKg;

    public void startVehicle() {
        engine.start();
    }
}

DeliveryVan now holds an Engine as a field rather than extending it — a has-a relationship. startVehicle() delegates to the engine's own start() method rather than inheriting it directly. This reads correctly and stays flexible: swapping in an ElectricEngine later only means changing one field's type.

A real business example: Fitness (Gym Equipment Bundles)

public class HeartRateMonitor {
    public Integer getCurrentBpm() {
        return 72; // placeholder for real sensor data
    }
}

public class Treadmill {
    private HeartRateMonitor monitor = new HeartRateMonitor();

    public String getWorkoutSummary() {
        return 'Current heart rate: ' + monitor.getCurrentBpm() + ' bpm';
    }
}

A Treadmill has a heart rate monitor built in — it definitely isn't a kind of heart rate monitor. Composition lets Treadmill reuse HeartRateMonitor's behavior without pretending they share a lineage they don't.

A rule of thumb

When deciding between extends and holding a field: say the relationship out loud. "A DeliveryVan is an Engine" sounds wrong; "a DeliveryVan has an Engine" sounds right. When "has-a" fits better than "is-a," reach for composition instead of inheritance — even though Apex would happily compile either one.

Exercise

Refactor this awkward inheritance (a Car extending a Radio) into composition: Car should hold a Radio as a field instead.

Show hint

private Radio radio = new Radio(); then delegate to it from a Car method.

APEX

Composition Over Inheritance Quiz

1. Composition models which kind of relationship?

2. A Car extending a Radio passes the "is-a" test cleanly.

Log in to submit the quiz and save your score.

My Notes

Log in to keep private notes on this lesson.

Questions about this lesson

No questions yet — be the first to ask.

Log in to ask a question about this lesson.

Summary

Composition builds a class by holding references to other objects ("has-a") instead of extending a parent class ("is-a") — often a more flexible fit when the relationship isn't a clean lineage.