Menu
Coddy logo textTech

Composition vs Inheritance

Part of the Object Oriented Programming section of Coddy's PHP journey — lesson 56 of 91.

In the previous chapter, you learned that traits provide a "has-a" capability while inheritance establishes an "is-a" relationship. Composition takes the "has-a" concept further by building complex objects from simpler ones, storing them as properties rather than inheriting from them.

With inheritance, a Car might extend Vehicle. With composition, a Car contains an Engine object:

<?php
class Engine {
    public function start(): string {
        return "Engine started";
    }
}

class Car {
    private Engine $engine;
    
    public function __construct() {
        $this->engine = new Engine();
    }
    
    public function start(): string {
        return $this->engine->start();
    }
}

$car = new Car();
echo $car->start();

Output:

Engine started

The Car class doesn't inherit from Engine - it owns one. This approach offers significant advantages: you can swap the engine for a different implementation, the classes remain loosely coupled, and you avoid the rigid hierarchies that deep inheritance creates.

A common guideline in OOP is "favor composition over inheritance." Inheritance works well for genuine type hierarchies (a Dog truly is an Animal), but composition shines when you're assembling functionality from independent parts. You can combine multiple objects, replace them at runtime, and test components in isolation.

The next lesson explores dependency injection, which builds directly on composition by controlling how objects receive their dependencies.

challenge icon

Challenge

Easy

Let's build a computer system using composition, where a Computer is assembled from separate component objects rather than inheriting from them. This demonstrates how composition allows you to build complex objects from simpler, independent parts.

You'll organize your code across three files:

  • Processor.php — Create a Processor class that represents a CPU. Use constructor promotion to define a private $model (string) and a private $cores (integer). Add a method getSpecs() that returns "[model] ([cores] cores)".
  • Memory.php — Create a Memory class that represents RAM. Use constructor promotion to define a private $size (integer representing GB). Add a method getCapacity() that returns "[size]GB RAM".
  • main.php — Include both component files and create a Computer class that uses composition. The Computer should:
    • Store a Processor object and a Memory object as private properties
    • Accept a processor model, core count, and memory size in its constructor
    • Create the component objects internally during construction
    • Have a describe() method that returns "Computer with [processor specs] and [memory capacity]"

    You'll receive three inputs: a processor model, a number of cores, and a memory size in GB. Create a Computer with these specifications and print the result of calling describe().

Notice how the Computer doesn't extend Processor or Memory — it contains them. This "has-a" relationship through composition keeps each component independent and reusable, while allowing the Computer to delegate to its parts when needed.

Cheat sheet

Composition builds complex objects from simpler ones by storing them as properties rather than inheriting from them. This creates a "has-a" relationship where one class contains instances of other classes.

Basic composition example:

<?php
class Engine {
    public function start(): string {
        return "Engine started";
    }
}

class Car {
    private Engine $engine;
    
    public function __construct() {
        $this->engine = new Engine();
    }
    
    public function start(): string {
        return $this->engine->start();
    }
}

The Car class doesn't inherit from Engine - it owns one. This keeps classes loosely coupled and avoids rigid inheritance hierarchies.

Favor composition over inheritance: Inheritance works well for genuine type hierarchies (a Dog is an Animal), but composition shines when assembling functionality from independent parts. You can combine multiple objects, replace them at runtime, and test components in isolation.

Try it yourself

<?php
require_once 'Processor.php';
require_once 'Memory.php';

class Computer
{
    // TODO: Define private properties to store Processor and Memory objects

    // TODO: Constructor should accept processor model, core count, and memory size
    // and create the component objects internally
    public function __construct()
    {
        // TODO: Create Processor and Memory objects and store them
    }

    // TODO: Implement describe() method that returns
    // "Computer with [processor specs] and [memory capacity]"
    public function describe(): string
    {
        // TODO: Delegate to component objects and return formatted string
    }
}

// Read input
$processorModel = trim(fgets(STDIN));
$cores = intval(fgets(STDIN));
$memorySize = intval(fgets(STDIN));

// TODO: Create a Computer with the given specifications and print the result of describe()

?>
quiz iconTest yourself

This lesson includes a short quiz. Start the lesson to answer it and track your progress.

All lessons in Object Oriented Programming