Single Responsibility Principle in object-oriented design (SOLID)

Author
By Darío Rivera
Posted On in SOLID

In a previous post, we saw an introduction to the SOLID Principles in a purely theoretical manner. Today, we will delve into the first principle in the SOLID list, The Single Responsibility Principle. Let's start by providing a definition of this principle.

Definition

The Single Responsibility Principle refers to the idea that a class should have only one job or responsibility. In other words, there should be only one reason to change a class. Classes with multiple responsibilities are difficult to maintain.

Example

Let's see a simple yet practical example in PHP. Suppose we have the classes Square and SquareCollection to represent a square and a collection of squares, respectively.

class Square
{
    private string $color;
    private int $size;

    public function __construct(string $color, int $size)
    {
        $this->color = $color;
        $this->size = $size;
    }

    public function getColor(): string
    {
        return $this->color;
    }

    public function getSize(): int
    {
        return $this->size;
    }
}
class SquareCollection
{
    /**
     * @var Square[]
     */
    private array $squares;

    public function getSquares(): array
    {
        return $this->squares;
    }

    public function addSquare(Square $square): void
    {
        $this->squares[] = $square;
    }
}

Now let's say we want to iterate over the squares in ascending order of size. For this, we have used the bubble sort algorithm, so our final code looks like this:

class SquareCollection
{
    /**
     * @var Square[]
     */
    private array $squares;

    public function getSquares(): array
    {
        return $this->squares;
    }

    public function addSquare(Square $square): void
    {
        $this->squares[] = $square;
    }

    /**
     * @return Square[]
     */
    protected function getOrdered(): array
    {
        return $this->bubbleSort($this->vegetables);
    }

    /**
     * @param Square[] $squares
     * @return Square[]
     */
    private function bubbleSort(array $squares): array
    {
        $size = count($squares);

        for ($i=0; $i<$size -1; $i++) {
            for ($j=0; $j<$size - 1 - $i; $j++) {
                $square = $squares[$j];
                $nextSquare = $squares[$j+1];

                if ($nextSquare->getSize() < $square->getSize()) {
                    $squares[$j+1] = $square;
                    $squares[$j] = $nextSquare;
                }
            }
        }

        return $squares;
    }
}

Although it works, we can see that the SquareCollection class has more than one responsibility or, in other words, several reasons to change. The first reason would be the storage of Square objects, including adding, replacing, deleting, etc. The next reason is the implementation of the sorting algorithm for these objects. In the future, we may not want to sort using bubble sort; perhaps we would like to sort in descending order or make adjustments to the current search algorithm.

Work Delegation

The solution to this issue is, of course, work delegation. Let's separate the bubble sort algorithm implementation from the collection.

class ArrayHelper
{
    /**
     * @param Square[] $squares
     * @return Square[]
     */
    public static function bubbleSort(array $squares): array
    {
        $size = count($squares);

        for ($i=0; $i<$size -1; $i++) {
            for ($j=0; $j<$size - 1 - $i; $j++) {
                $square = $squares[$j];
                $nextSquare = $squares[$j+1];

                if ($nextSquare->getSize() < $square->getSize()) {
                    $squares[$j+1] = $square;
                    $squares[$j] = $nextSquare;
                }
            }
        }

        return $squares;
    }
}

Now let's use this helper class in our collection class.

class SquareCollection
{
    /**
     * @var Square[]
     */
    private array $squares;

    public function getSquares(): array
    {
        return $this->squares;
    }

    public function addSquare(Square $square): void
    {
        $this->squares[] = $square;
    }

    /**
     * @return Square[]
     */
    protected function getOrdered(): array
    {
        return ArrayHelper::bubbleSort($this->vegetables);
    }
}

This way, the responsibility of sorting the collection using bubble sort has been delegated to another helper class and is not directly in the collection class anymore.

Although this code is good, we can go a step further and use the iterator pattern to completely remove the getOrdered method from the collection. I invite you to check out the following GitHub repository with a similar implementation:

- Iterator pattern in depth


Acerca de Darío Rivera

Author

Application Architect at Elentra Corp . Quality developer and passionate learner with 10+ years of experience in web technologies. Creator of EasyHttp , an standard way to consume HTTP Clients.

LinkedIn Twitter Instagram

Sólo aquellos que han alcanzado el éxito saben que siempre estuvo a un paso del momento en que pensaron renunciar.