Composition (“has-a”) or Inheritance (“is-a” ), how to choose the corret relationship?
First of all, you have to know that, in the scenario of modern object-oriented design principles, there is a strong preference for Composition over Inheritance as better design principle.
This happens because, once you extend a class (inherit from it), you become bound to its structure and behavior, making it difficult to change it later. Let’s see a brief about each relantionship below.
“Is-a” → Inheritance
Used when one class is a type of another class.
Example:Car is-a Vehicle → class Car extends Vehicle
“Has-a” → Composition / Aggregation
Used when one class contains or uses another class.
Example:Car has-a Engine → class Car { private Engine $engine; }
How to choose?
- Use is-a when you want to reuse behavior and the relationship is naturally hierarchical.
- Use has-a when you want flexibility, replaceable components, and loose coupling.
Code exemples
IS-A (Inheritance) – Use when a class is a type of another class.
<?php
class Vehicle
{
public function move()
{
echo "The vehicle is moving\n";
}
}
class Car extends Vehicle // Car *is-a* Vehicle
{
}
$car = new Car();
$car->move(); // inheritedHAS-A (Composition) – Use when a class contains another class.
<?php
class Engine
{
public function start()
{
echo "Engine started\n";
}
}
class Car
{
private Engine $engine; // Car *has-a* Engine
public function __construct()
{
$this->engine = new Engine();
}
public function turnOn()
{
$this->engine->start();
}
}
$car = new Car();
$car->turnOn();Now, let’s improve the HAS-A, with a more flexible and modern example using interfaces + dependency injection, allowing a car to use diesel, gasoline, ethanol (alcohol), electric, or any other engine.
//Interface
<?php
interface EngineInterface
{
public function start(): void;
}
//Different engines
class DieselEngine implements EngineInterface
{
public function start(): void
{
echo "Diesel engine roaring...\n";
}
}
class GasEngine implements EngineInterface
{
public function start(): void
{
echo "Gas engine starting...\n";
}
}
class AlcoholEngine implements EngineInterface
{
public function start(): void
{
echo "Alcohol engine warming up...\n";
}
}
//Car uses composition + dependency injection
class Car
{
private EngineInterface $engine; // Car *has-a* Engine
public function __construct(EngineInterface $engine)
{
$this->engine = $engine;
}
public function turnOn(): void
{
$this->engine->start();
}
}
//Using it...
$RangeRover = new Car(new DieselEngine());
$RangeRover->turnOn();
$Civic = new Car(new GasEngine());
$Civic->turnOn();
$Fiat147 = new Car(new AlcoholEngine());
$Fiat147->turnOn();
/*
Output:
Diesel engine roaring...
Gas engine starting...
Alcohol engine warming up...
*/
Leave a Reply