Inheritance vs. Composition in PHP π
When programming with Object-Oriented Programming (OOP), it's important to understand the difference between Inheritance and Composition:
Inheritance: It's one of the fundamental principles of OOP and allows a class to inherit properties and methods from another class. In simple terms, inheritance enables the creation of a new class (called a subclass or child class) that reuses, extends, or modifies the behavior of an existing class (called a superclass or parent class).
<?php
// Defining an Engine class that has a start method
class Engine
{
// The start method simulates the operation of an engine
public function start()
{
return "Engine Started!";
}
}
// The Car class extends the Engine class, meaning it inherits
// all properties and methods from the Engine class
class Car extends Engine
{
// Here, we are overriding the start method from the Engine class
// This allows us to customize the behavior of the method in the child class
public function start()
{
return "The car engine is started!";
}
}
// We create an instance of the Car class
$car = new Car();
// Since the start method was overridden in the Car class, it executes the new behavior
echo $car->start(); // Output: The car engine is started!
Composition Now, imagine that we are modeling a car that is composed of several parts, like an engine and wheels. Instead of creating a single "Car" class with all the details, we can break down these functionalities into smaller classes. The car will be composed of objects from other classes, such as an Engine.
<?php
// Defining an Engine class with the start method
class Engine
{
// The start method simulates the operation of an engine
public function start()
{
return "Engine Started!";
}
}
// The Car class is composed of an Engine object, rather than inheriting from Engine
class Car
{
// Private property to store the instance of Engine
private Engine $engine;
// The constructor receives an instance of Engine as a dependency
// This is an example of dependency injection
public function __construct(Engine $engine)
{
// We assign the Engine object to the private property
$this->engine = $engine;
}
// The start method delegates the responsibility of starting the engine to the Engine object
public function start()
{
// Here we call the start method of the Engine object
return $this->engine->start();
}
}
// We create an instance of Engine
$v4Engine = new Engine();
// We create an instance of Car, passing the Engine as a parameter
$car = new Car($v4Engine);
// When we call the start method, it delegates the task to the Engine object
echo $car->start(); // Output: Engine Started!
Understanding Instead of the Car class containing the logic to start the engine directly, it delegates that responsibility to the Engine object. This keeps the Car class focused on its own responsibility, making the code more modular and easier to maintain. In the future, you can modify or improve the Engine without needing to change the Car class's code.
Advantages This approach is more flexible because it allows the car to have different types of engines (for example, a V4, V6, or electric engine) without needing to change the Car class. This modularity makes it easier to maintain and expand the system.
You can run the code at https://onecompiler.com/php
Top comments (2)
Great post.
I would change the Engine class typing, in the second Car example, with an Engine interface.
Using an interface gives you a lot more room to specify the different characteristics for the engines.
You mention V4, V6 and electric engine, but with a class you have to extend the class each time a new engine time has to added. With an interface you can create a separate class for every engine.
with Engine interface
In this example I added a CombustionEngine class to be the parent of the V4Egine and V6Engine.
So it is mix of inheritance and composition. This it how all codebases are structured.
Use both features of OOP when they are appropriate.
Nice