Composition means building a class out of other objects it contains as properties, rather than by inheriting from a parent class — a 'has-a' relationship instead of an 'is-a' relationship.
The classic beginner mistake — modeling 'has-a' with inheritance:
class Engine {
function start() { print("Engine starting...") }
}
class Car extends Engine { // WRONG — a Car is not a kind of Engine
function drive() {
this.start() // works, but conceptually backwards
}
}
A Car has an Engine; it is not an Engine. Using inheritance here is a category error, even though it technically compiles and runs.
The composition fix:
class Engine {
function start() { print("Engine starting...") }
}
class Car {
private engine
function __construct(engine) {
this.engine = engine // Car HAS an Engine, stored as a property
}
function drive() {
this.engine.start() // delegates to the contained object
print("Car is driving.")
}
}
myCar = new Car(new Engine())
myCar.drive()
A simple test to decide between them: ask 'is a Car fundamentally a specialized kind of Engine?' The answer is clearly no — a car has many parts, an engine among them, but a car isn't a specialized engine. Compare that to Sedan extends Car — a Sedan genuinely is a kind of Car, sharing all its fundamental behavior while specializing some of it. That's a correct use of inheritance.
The widely-cited OOP design guideline 'favor composition over inheritance' doesn't mean inheritance is bad — it means: default to composition unless you can clearly justify a genuine, stable 'is-a' relationship, because composition tends to produce more flexible, loosely-coupled, easier-to-change code.