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 startedThe 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
EasyLet'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 aProcessorclass that represents a CPU. Use constructor promotion to define a private$model(string) and a private$cores(integer). Add a methodgetSpecs()that returns"[model] ([cores] cores)".Memory.php— Create aMemoryclass that represents RAM. Use constructor promotion to define a private$size(integer representing GB). Add a methodgetCapacity()that returns"[size]GB RAM".main.php— Include both component files and create aComputerclass that uses composition. TheComputershould:- Store a
Processorobject and aMemoryobject 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
Computerwith these specifications and print the result of callingdescribe().- Store a
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()
?>This lesson includes a short quiz. Start the lesson to answer it and track your progress.
All lessons in Object Oriented Programming
1Fundamentals of OOP
External FilesIntroduction to OOPClasses vs ObjectsThe $this KeywordMethodsPropertiesConstructor (__construct)Destructor (__destruct)Recap - Simple Calculator4Inheritance
Basic InheritanceThe parent:: KeywordMethod OverridingThe final KeywordAbstract ClassesRecap - Employee Hierarchy7Encapsulation
Public, Protected, PrivateAccess Modifiers in DepthGetters and SettersInformation HidingConstructor Promotion (8.0)Recap - Student Records System10Advanced OOP Concepts
Composition vs InheritanceDependency InjectionAnonymous ClassesEnums (PHP 8.1)Fibers (PHP 8.1)Object Cloning Deep DiveGenerators & Iterators2Namespaces & Autoloading
Introduction to NamespacesThe use KeywordPSR-4 Autoloading StandardComposer AutoloaderRecap - Organized Project5Interfaces & Contracts
Introduction to InterfacesImplementing InterfacesMultiple Interface ImplementInterface vs Abstract ClassType Hinting with InterfacesRecap - Shape Calculator8Magic Methods
Magic Methods Introduction__toString & __debugInfo__get, __set, __isset, __unset__call & __callStatic__clone & Object Cloning__serialize & __unserializeRecap - Custom Collection11Type System & Error Handling
Type DeclarationsNullable TypesUnion & Intersection TypesException ClassesCustom Exception HierarchyTry, Catch, FinallyRecap - Form Validator14Project: Library Management
Project OverviewBook and User Classes3Class Properties
Instance vs Static PropertiesConstants in ClassesStatic Methods & PropertiesPrivate & Protected PropertiesReadonly Properties (PHP 8.1)Recap - Bank Account Manager6Polymorphism
Method Overriding RevisitedPolymorphism via InterfacesType Hinting & Union TypesLate Static BindingRecap - Payment Processor9Traits
Introduction to TraitsUsing Multiple TraitsTrait Conflict ResolutionAbstract Methods in TraitsTraits vs Inheritance12Design Patterns Part 1
Intro to Design PatternsSingleton PatternFactory PatternObserver PatternStrategy Pattern