Composition vs. Inheritance

Composition vs. Inheritance in PHP
When designing software in PHP, or any object-oriented programming language, choosing between composition and inheritance is a crucial decision that can impact the flexibility, maintainability, and scalability of your code.

This article delves into understanding composition and inheritance, their differences, and when to use each approach, supplemented with practical PHP examples.

Understanding Inheritance

What is Inheritance?

Inheritance is a mechanism where a new class, known as a child or subclass, is derived from an existing class, known as a parent or superclass. The child class inherits the properties and methods of the parent class, allowing for code reuse and the creation of hierarchical relationships.

Example: Inheritance in PHP

				
					class Animal {
    public $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function speak() {
        echo "$this->name makes a sound.\n";
    }
}

class Dog extends Animal {
    public function speak() {
        echo "$this->name barks.\n";
    }
}

$dog = new Dog("Buddy");
$dog->speak(); // Output: Buddy barks.

				
			

In this example, the Dog class inherits from the Animal class and overrides the speak method to provide specific behavior for dogs.

Benefits of Inheritance

Code Reuse: Allows reuse of existing code, reducing redundancy.
Hierarchical Relationships: Models real-world relationships naturally (e.g., Dog is an Animal).
Polymorphism: Enables polymorphic behavior, allowing child classes to be treated as instances of the parent class.

Drawbacks of Inheritance

Tight Coupling: Increases coupling between parent and child classes, making changes in the parent class potentially impact all child classes.
Fragile Base Class Problem: Changes in the base class can have unintended consequences on derived classes.
Limited Flexibility: Inheritance models “is-a” relationships strictly, which might not be suitable for all scenarios.

Understanding Composition

What is Composition?

Composition is a design principle where a class is composed of one or more objects from other classes, allowing it to use their functionality. This approach models “has-a” relationships and promotes greater flexibility by combining behaviors and properties of multiple classes.

Example: Composition in PHP

				
					class Engine {
    public function start() {
        echo "Engine starts.\n";
    }
}

class Car {
    private $engine;

    public function __construct(Engine $engine) {
        $this->engine = $engine;
    }

    public function start() {
        $this->engine->start();
        echo "Car starts.\n";
    }
}

$engine = new Engine();
$car = new Car($engine);
$car->start(); // Output: Engine starts. Car starts.

				
			

In this example, the Car class uses an instance of the Engine class, demonstrating a “has-a” relationship.

Benefits of Composition

Flexibility: Allows combining behaviors and properties from multiple classes without forming rigid hierarchical relationships.
Loose Coupling: Reduces dependencies between classes, making the code easier to maintain and extend.
Reusable Components: Encourages the creation of reusable components that can be combined in various ways.

Drawbacks of Composition

More Boilerplate Code: May require more code to set up and manage dependencies.
Complex Relationships: Can lead to complex relationships and interactions between objects if not managed properly.

Comparing Composition and Inheritance

Use Cases for Inheritance

Hierarchical Class Structures: When you need to model hierarchical relationships where the child class is a specialized form of the parent class.
Polymorphic Behavior: When you need to treat objects of different classes uniformly, typically when using polymorphism.
Shared Behavior: When multiple classes share significant common behavior and properties.

Example: When to Use Inheritance

				
					abstract class Shape {
    protected $color;

    public function __construct($color) {
        $this->color = $color;
    }

    abstract public function draw();
}

class Circle extends Shape {
    public function draw() {
        echo "Drawing a $this->color circle.\n";
    }
}

class Square extends Shape {
    public function draw() {
        echo "Drawing a $this->color square.\n";
    }
}

$shapes = [
    new Circle("red"),
    new Square("blue")
];

foreach ($shapes as $shape) {
    $shape->draw();
}

				
			

In this example, inheritance is used to create a hierarchy of shapes that share common properties and behaviors.

Use Cases for Composition

Dynamic Behavior Changes: When you need to dynamically change behavior by composing different objects.
Code Reuse without Hierarchy: When you want to reuse code without forming a rigid class hierarchy.
Decoupling: When you aim to decouple classes to enhance flexibility and maintainability.

Example: When to Use Composition

				
					class Notification {
    private $message;

    public function __construct($message) {
        $this->message = $message;
    }

    public function send() {
        echo "Sending notification: $this->message\n";
    }
}

class User {
    private $notification;

    public function __construct(Notification $notification) {
        $this->notification = $notification;
    }

    public function notify() {
        $this->notification->send();
    }
}

$notification = new Notification("Welcome!");
$user = new User($notification);
$user->notify(); // Output: Sending notification: Welcome!

				
			

In this example, composition is used to add notification functionality to the User class without forming a hierarchical relationship.

Combining Composition and Inheritance

In practice, you often combine both composition and inheritance to leverage their respective strengths. For instance, you might use inheritance to define a basic class hierarchy and composition to add or extend functionalities dynamically.

Example: Combining Composition and Inheritance

				
					abstract class Employee {
    protected $name;

    public function __construct($name) {
        $this->name = $name;
    }

    abstract public function work();
}

class Developer extends Employee {
    public function work() {
        echo "$this->name is writing code.\n";
    }
}

class Designer extends Employee {
    public function work() {
        echo "$this->name is designing a layout.\n";
    }
}

class Project {
    private $employees = [];

    public function addEmployee(Employee $employee) {
        $this->employees[] = $employee;
    }

    public function start() {
        foreach ($this->employees as $employee) {
            $employee->work();
        }
    }
}

$project = new Project();
$project->addEmployee(new Developer("Alice"));
$project->addEmployee(new Designer("Bob"));
$project->start();

				
			

In this example, inheritance is used to define different types of employees, while composition is used to manage employees within a project.

Conclusion

Both composition and inheritance are essential tools in an object-oriented programmer’s toolbox. Understanding when to use each approach can significantly enhance the design, maintainability, and scalability of your code. Inheritance is suitable for modeling hierarchical relationships and shared behavior, while composition offers greater flexibility and promotes code reuse without rigid class hierarchies.

By leveraging the strengths of both patterns, you can create robust and flexible applications in PHP. Whether you choose inheritance, composition, or a combination of both, the key is to focus on reducing coupling, enhancing reusability, and maintaining a clean and manageable codebase.

Scroll to Top