What is Composition?

Composition is an OOP design technique where a class is built by containing instances of other classes as attributes, rather than inheriting from them. It models a “HAS-A” relationship — a Car HAS-A Engine. When the parent object is destroyed, the composed objects are destroyed with it (strong ownership — unlike Aggregation).

Explanation

Real-World Analogy

  • A Computer 🖥️ is composed of a CPU, RAM, HardDrive, and GraphicsCard. The computer HAS-A CPU. When the computer is scrapped (destroyed), all its components are destroyed with it — they have no independent existence apart from this computer.
CompositionOOP Equivalent
Computer HAS-A CPUclass Computer: def __init__: self.cpu = CPU()
CPU can’t work without Computer hereComposed object lifecycle tied to owner
Computer is not a CPUNo inheritance — HAS-A not IS-A

Composition vs Inheritance

AspectCompositionInheritance
RelationshipHAS-AIS-A
CouplingLoose — components are replaceableTight — child locked to parent structure
Flexibility✅ Swap implementations at runtime❌ Fixed at compile time
Code ReuseVia delegation to inner objectsVia inherited methods
Testability✅ Inject mock components easily❌ Harder to mock parent behavior
When to prefer”Favor composition over inheritance” (GoF)Clear IS-A hierarchy
  • "Favor Composition Over Inheritance" — one of the most important software design principles from the Gang of Four Design Patterns book. Composition leads to more flexible, testable, maintainable code.

Composition vs Aggregation

CompositionAggregation
OwnershipStrong — child cannot exist without parentWeak — child can exist independently
LifecycleChild destroyed with parentChild survives parent destruction
ExampleCar + Engine (Engine created inside Car)Team + Player (Player exists without team)
UML symbol◆ (filled diamond)◇ (empty diamond)

Implementation

  • A Car composed of Engine, Transmission, and GPS — showing composition, delegation, and dependency injection. Languages: Python · Cpp · Java · Java Script · CSharp

# ─── Python ──────────────────────────────────────────────────────────
from dataclasses import dataclass, field
 
@dataclass
class Engine:
    cylinders: int
    horsepower: int
    _running: bool = field(default=False, repr=False)
 
    def start(self) -> str:
        self._running = True
        return f"Engine ({self.cylinders}cyl, {self.horsepower}hp) started 🔥"
 
    def stop(self) -> str:
        self._running = False
        return "Engine stopped."
 
    def is_running(self) -> bool:
        return self._running
 
 
@dataclass
class GPS:
    provider: str = "Google Maps"
 
    def navigate_to(self, destination: str) -> str:
        return f"[{self.provider}] Navigating to {destination}..."
 
 
@dataclass
class Transmission:
    type_: str = "Automatic"  # "Manual" | "Automatic" | "CVT"
    current_gear: int = 0
 
    def shift_up(self) -> str:
        self.current_gear += 1
        return f"{self.type_} → Gear {self.current_gear}"
 
 
class Car:
    """Car HAS-A Engine, Transmission, GPS — all created and owned by Car."""
 
    def __init__(self, make: str, model: str,
                 engine: Engine = None,    # ← Dependency Injection
                 gps: GPS = None):
        self.make = make
        self.model = model
        # Composition — Car owns these:
        self.engine = engine or Engine(4, 200)       # default engine
        self.transmission = Transmission("Automatic") # always created here
        self.gps = gps or GPS()                      # default GPS
 
    def start(self) -> str:
        return f"{self.make} {self.model}: {self.engine.start()}"
 
    def navigate(self, destination: str) -> str:
        if not self.engine.is_running():
            return "Start the car first!"
        return self.gps.navigate_to(destination)
 
    def shift(self) -> str:
        return self.transmission.shift_up()
 
    def __repr__(self) -> str:
        return f"Car({self.make} {self.model}, {self.engine.horsepower}hp)"
 
 
# Standard usage
car1 = Car("Toyota", "Supra")
print(car1.start())             # Toyota Supra: Engine (4cyl, 200hp) started 🔥
print(car1.navigate("Airport")) # [Google Maps] Navigating to Airport...
print(car1.shift())             # Automatic → Gear 1
 
# Dependency injection — inject a custom engine
v8 = Engine(8, 500)
car2 = Car("Ford", "Mustang", engine=v8, gps=GPS("Waze"))
print(car2)                     # Car(Ford Mustang, 500hp)
print(car2.start())             # Ford Mustang: Engine (8cyl, 500hp) started 🔥
// ─── C++ ─────────────────────────────────────────────────────────────
#include <iostream>
#include <string>
#include <memory>
 
class Engine {
    int cylinders_, hp_;
    bool running_ = false;
public:
    Engine(int cyl, int hp) : cylinders_(cyl), hp_(hp) {}
    std::string start() {
        running_ = true;
        return "Engine (" + std::to_string(cylinders_) + "cyl, "
                          + std::to_string(hp_) + "hp) started";
    }
    bool isRunning() const { return running_; }
    ~Engine() { std::cout << "[Engine destroyed]\n"; }
};
 
class GPS {
    std::string provider_;
public:
    GPS(std::string provider = "Google Maps") : provider_(provider) {}
    std::string navigateTo(const std::string& dest) {
        return "[" + provider_ + "] Navigating to " + dest;
    }
};
 
class Car {
    std::string make_, model_;
    Engine engine_;          // Composition — value type (owned)
    GPS gps_;                // Composition — value type (owned)
public:
    Car(std::string make, std::string model, int cyl = 4, int hp = 200)
        : make_(make), model_(model), engine_(cyl, hp), gps_() {}
 
    std::string start() { return make_ + " " + model_ + ": " + engine_.start(); }
    std::string navigate(const std::string& dest) {
        if (!engine_.isRunning()) return "Start car first!";
        return gps_.navigateTo(dest);
    }
    ~Car() { std::cout << "[Car " << make_ << " destroyed]\n"; }
};
 
int main() {
    Car car("Toyota", "Supra");
    std::cout << car.start() << "\n";
    std::cout << car.navigate("Airport") << "\n";
}
// Destructors: [Car Toyota destroyed] then [Engine destroyed]
// ─── Java ─────────────────────────────────────────────────────────────
public class CompositionDemo {
 
    static class Engine {
        int cylinders, hp;
        boolean running = false;
        Engine(int cyl, int hp) { this.cylinders = cyl; this.hp = hp; }
        String start() { running = true; return "Engine (" + cylinders + "cyl, " + hp + "hp) started"; }
        boolean isRunning() { return running; }
    }
 
    static class GPS {
        String provider;
        GPS(String provider) { this.provider = provider; }
        GPS() { this("Google Maps"); }
        String navigateTo(String dest) { return "[" + provider + "] Navigating to " + dest; }
    }
 
    static class Car {
        String make, model;
        Engine engine;     // Composition
        GPS gps;           // Composition
 
        Car(String make, String model) {
            this(make, model, new Engine(4, 200), new GPS());
        }
 
        Car(String make, String model, Engine engine, GPS gps) {
            this.make = make; this.model = model;
            this.engine = engine; this.gps = gps;  // Dependency Injection
        }
 
        String start() { return make + " " + model + ": " + engine.start(); }
        String navigate(String dest) {
            if (!engine.isRunning()) return "Start car first!";
            return gps.navigateTo(dest);
        }
    }
 
    public static void main(String[] args) {
        Car car1 = new Car("Toyota", "Supra");
        System.out.println(car1.start());
        System.out.println(car1.navigate("Airport"));
 
        // Dependency injection
        Car car2 = new Car("Ford", "Mustang", new Engine(8, 500), new GPS("Waze"));
        System.out.println(car2.start());
    }
}
// ─── JavaScript ───────────────────────────────────────────────────────
class Engine {
    constructor(cylinders, hp) {
        this.cylinders = cylinders;
        this.hp = hp;
        this.running = false;
    }
    start() { this.running = true; return `Engine (${this.cylinders}cyl, ${this.hp}hp) started 🔥`; }
    isRunning() { return this.running; }
}
 
class GPS {
    constructor(provider = "Google Maps") { this.provider = provider; }
    navigateTo(dest) { return `[${this.provider}] Navigating to ${dest}...`; }
}
 
class Car {
    constructor(make, model, engine = new Engine(4, 200), gps = new GPS()) {
        this.make = make;
        this.model = model;
        this.engine = engine;   // Composition
        this.gps = gps;         // Composition (Dependency Injection)
    }
 
    start() { return `${this.make} ${this.model}: ${this.engine.start()}`; }
 
    navigate(dest) {
        return this.engine.isRunning()
            ? this.gps.navigateTo(dest)
            : "Start the car first!";
    }
 
    toString() { return `Car(${this.make} ${this.model}, ${this.engine.hp}hp)`; }
}
 
const car1 = new Car("Toyota", "Supra");
console.log(car1.start());              // Toyota Supra: Engine (4cyl, 200hp) started 🔥
console.log(car1.navigate("Airport"));  // [Google Maps] Navigating to Airport...
 
const car2 = new Car("Ford", "Mustang", new Engine(8, 500), new GPS("Waze"));
console.log(car2.toString());           // Car(Ford Mustang, 500hp)
// ─── C# ──────────────────────────────────────────────────────────────
using System;
 
class Engine {
    public int Cylinders { get; }
    public int Hp { get; }
    public bool Running { get; private set; }
 
    public Engine(int cyl, int hp) { Cylinders = cyl; Hp = hp; }
    public string Start() { Running = true; return $"Engine ({Cylinders}cyl, {Hp}hp) started"; }
}
 
class GPS {
    string provider;
    public GPS(string provider = "Google Maps") { this.provider = provider; }
    public string NavigateTo(string dest) => $"[{provider}] Navigating to {dest}";
}
 
class Car {
    string make, model;
    Engine engine;   // Composition
    GPS gps;         // Composition
 
    public Car(string make, string model, Engine engine = null, GPS gps = null) {
        this.make = make; this.model = model;
        this.engine = engine ?? new Engine(4, 200);
        this.gps = gps ?? new GPS();
    }
 
    public string Start() => $"{make} {model}: {engine.Start()}";
    public string Navigate(string dest) =>
        engine.Running ? gps.NavigateTo(dest) : "Start the car first!";
 
    static void Main() {
        var car1 = new Car("Toyota", "Supra");
        Console.WriteLine(car1.Start());
        Console.WriteLine(car1.Navigate("Airport"));
 
        var car2 = new Car("Ford", "Mustang", new Engine(8, 500), new GPS("Waze"));
        Console.WriteLine(car2.Start());
    }
}

Key Takeaways

  • Composition = HAS-A relationship — an object contains other objects as its components.
  • Strong ownership — composed objects are created and destroyed with the parent (unlike Aggregation).
  • Favor Composition Over Inheritance — composition is more flexible, testable, and avoids tight coupling.
  • Dependency Injection — pass composed objects via the constructor to make them swappable (great for testing).
  • Composition delegates behavior to inner objects — the outer class acts as an orchestrator.

More Learn

GitHub & Webs