Solid Principles in PHP – Open-Closed

The O in SOLID represents the open-closed principle which states that entities should be open for extension but closed for modifications. What this essentially means is how can we change the behaviour of our class without modifying the source code. We strive to achieve this to avoid code rot. So our aim is to modify the behaviour by doing it through extension and not by modifing the source code.

Let’s dive into the code. We have a square class and we have been asked to calculate the area of the square.

class Square
{
    protected $width;
    protected $height;

    public function __construct($width, $height)
    {
        $this->width = $width;
        $this->height = $height;
    }
}

We’ll create a a separate class to calculate the area.

class AreaCalculator
{
    public function calculate($squares)
    {
        foreach ($squares as $square) {
            $area[] = $square->width * $square->height;
        }

        return array_sum($area);
    }
}

All seems well until the requirements have come in and now we need to calculate the area of a circle. As you are well aware that to calculate the area of a circle is different to a square and to accomodate for this, most developers will alter the area calculator class with a series of if statements. Modifying this class violates the open-closed principle.

Lets changes the AreaCalculator class yo the way uninformed developers will likely integrate the calculation of an area for a circle.

public function calculate($shapes)
{
    foreach ($shapes as $shape) {
        if (is_a($shape, 'Square')){
            $area[] = $shape->width * $shape->height;
        }
        else {
            $area[] = $shape->area * $shape->area * pi();
        }
    }

    return array_sum($area);
}

You will see pretty quickly that if we keep on adding different shapes that this class will start getting ugly and eventually issues will occur and the code will begin to rot.

So how to we extend the behaviour while keeping the class closed for modification? When you have a module that you want to extend without modifying, separate the extensible behaviour behind an interface, and flip the dependencies. The first step is to add an interface and lets call this ShapreInterface.php

interface ShapeInterface
{
    public function area();
}

Now we have to implement the interface with each of the concrete classes.

class Square implements ShapeInterface
{
    public $width;
    public $height;

    public function __construct($width, $height)
    {
        $this->width = $width;
        $this->height = $height;
    }

    public function area()
    {
        return $this->width * $this->height;
    }
}

class Circle implements ShapeInterface
{
    protected $radius;

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

    public function area()
    {
        return $this->radius * $this->radius * pi();
    }
}

class AreaCalculator
{
    public function calculate($shapes)
    {
        foreach ($shapes as $shape) {
            $area[] = $shape->area();
        }

        return array_sum($area);
    }
}

Now when we want to add another shape say a triangle we don’t need to modify the AreaCalculator class all we need to do is add a Triangle class which implements the ShapeInterface and add our own calculation for area for the triangle.

Leave a Reply

Your email address will not be published.