Classes: comparison of the “is-a” vs “has-a” relationship

·

,

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 Vehicleclass Car extends Vehicle


“Has-a” → Composition / Aggregation
Used when one class contains or uses another class.

Example:
Car has-a Engineclass 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(); // inherited

HAS-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...
*/

Create your own user feedback survey

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *