PHP - - By Alejandro Gervasio

Robust software systems should be built up from a web of interrelated objects whose responsibilities are tight, neatly cohesive, boiled down to just performing a few narrowed and well-defined tasks. However, it’s admittedly pretty difficult to design such systems, at least in the first take. Most of the time we tend to group tasks in question by following an ad-hoc semantic sense in consonance with the nature of our own human mind.

One of the most notorious consequences of this rational associative process is that, at some point, we effectively end up creating classes that do too much. The so-called “God class” is quite possibly the most extreme and coarse example of a structure that packages literally piles of unrelated operations behind the fence of the same API, but there are other subtle, more furtive situations where assigning of multiple roles to the same class are harder to track down. A good example of this is the Singleton.

We all know that Singletons have been condemned for years because they suffer all sort of nasty implementation issues, with the classic mutable global state by far the most infamous one. They can be also be blamed for doing two semantically-unrelated things at the same time: asides from playing their main role, whatever this might be, they’re responsible for controlling how the originating classes should be instantiated as well. Singletons are entities unavoidably cursed with the obligation of performing at least two different tasks which aren’t even remotely related to each other.

It’s easy to stay away from Singletons without feeling a pinch of guilt. But how can one be pragmatic in more generic, day-to-day situations, and design classes whose concerns are decently cohesive? Even when there’s no straight answer to the question, it’s possible to adhere in general to the rules of the Single Responsibility Principle, whose formal definition states the following:

There should never be more than one reason for a class to change.

What the principle attempts to promote is that classes must always be designed to expose only one area of concern and the set of operations they define and implement must be aimed at fulfilling that concern in particular and nothing else. In other words, a class should only change in response to the execution of those semantically-related operations. If it ever needs to change in response to another, totally unrelated operation, then it’s clear the class has more than one responsibility.

Let’s move along and code a few digestible examples so that we can see how to take advantage of the principle’s benefits.

A Typical Violation of the Single Responsibility Principle

For obvious reasons, there’s plenty of situations where a seemingly-cohesive set of operations assigned to a class actually scope different unrelated responsibilities, hence violating the principle. One that I find particularly instructive is the Active Record Pattern because of the momentum it has gained in the last few years.

Let’s pretend we’re a blind worshiper of the pattern and of the so-called database model and want to appeal to its niceties for creating a basic entity class which should model the data and behavior of generic users. Such a class, along with the contract that it implements, could be defined as follows:

<?php
namespace Model;

interface UserInterface
{
    public function setId($id);
    public function getId();

    public function setName($name);
    public function getName();

    public function setEmail($email);
    public function getEmail();
    public function getGravatar();

    public function findById($id);
    public function insert();
    public function update();
    public function delete();
}
<?php
namespace Model;
use LibraryDatabaseDatabaseAdapterInterface;

class User implements UserInterface
{
    private $id;
    private $name;
    private $email;
    private $db;
    private $table = "users";

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

    public function setId($id) {
        if ($this->id !== null) {
            throw new BadMethodCallException(
                "The user ID has been set already.");
        }
        if (!is_int($id) || $id < 1) {
            throw new InvalidArgumentException(
                "The user ID is invalid.");
        }
        $this->id = $id;
        return $this;
    }
    
    public function getId() {
        return $this->id;
    }
    
    public function setName($name) {
        if (strlen($name) < 2 || strlen($name) > 30) {
            throw new InvalidArgumentException(
                "The user name is invalid.");
        }
        $this->name = $name;
        return $this;
    }
    
    public function getName() {
        if ($this->name === null) {
            throw new UnexpectedValueException(
                "The user name has not been set.");
        }
        return $this->name;
    }

    public function setEmail($email) {
        if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
            throw new InvalidArgumentException(
                "The user email is invalid.");
        }
        $this->email = $email;
        return $this;
    }
    
    public function getEmail() {
        if ($this->email === null) {
            throw new UnexpectedValueException(
                "The user email has not been set.");
        }
        return $this->email;
    }
    
    public function getGravatar($size = 70, $default = "monsterid") {
        return "http://www.gravatar.com/avatar/" .
            md5(strtolower($this->getEmail())) .
            "?s=" . (integer) $size .
            "&d=" . urlencode($default) .
            "&r=G";
    }
    
    public function findById($id) {
        $this->db->select($this->table,
            ["id" => $id]);
        if (!$row = $this->db->fetch()) {
            return null;
        }
        $user = new User($this->db);
        $user->setId($row["id"])
             ->setName($row["name"])
             ->setEmail($row["email"]);
        return $user;
    }
    
    public function insert() {
        $this->db->insert($this->table, [
            "name"  => $this->getName(), 
            "email" => $this->getEmail()
        ]);
    }
    
    public function update() {
        $this->db->update($this->table, [
                "name"  => $this->getName(), 
                "email" => $this->getEmail()], 
            "id = {$this->id}");
    }

    public function delete() {
        $this->db->delete($this->table,
            "id = {$this->id}");
    }
}

As one might expect from a typical implementation of Active Record, the User class is pretty much a messy structure which mingles chunks of business logic such as setting/getting usernames and email addresses, and even generating some nifty Gravatars on the fly, with data access. Is this a violation of the Single Responsibility Principle? Well, unquestionably it is, as the class exposes to the outside world two different responsibilities which by no means have a true semantic relationship with each other.

Even without going through the class’ implementation and just scanning its interface, it’s clear to see that the CRUD methods should be placed in the data access layer completely insulated from where the mutators/accessors live and breath. In this case, the result of executing the findById() method for instance will change the state of the class while any call to the setters impact the CRUD operations as well. This implies there are two overlapped responsibilities coexisting here which makes the class change in response to different requirements.

Of course, if you’re anything like me you’ll be wondering how to turn User into an Single Responsibility Principle-compliant structure without too much hassle during the refactoring process. The first modification that should be introduced is to keep all the domain logic within the class’ boundaries while moving away the one that deals with data access… yes, to the data access layer. There are a few nifty ways to accomplish this, but considering that the responsibilities should be sprinkled across multiple layers, the use of a data mapper is an efficient approach that permits to do this in a fairly painless fashion.

Putting Data Access Logic in a Data Mapper

The best way to keep the class’ responsibilities (the domain-related ones, of course) isolated from the ones that deal with data access is via a basic data mapper. The one below does a decent job when it comes to dividing up the responsibilities in question:

<?php
namespace Mapper;
use ModelUserInterface;

interface UserMapperInterface
{
    public function findById($id);
    public function insert(UserInterface $user);
    public function update(UserInterface $user);
    public function delete($id);
}
<?php
namespace Mapper;
use LibraryDatabaseDatabaseAdapterInterface,
    ModelUserInterface,
    ModelUser;

class UserMapper implements UserMapperInterface
{
    private $db;
    private $table = "users";
    
    public function __construct(DatabaseAdapterInterface $db) {
        $this->db = $db;
    }
    
    public function findById($id) {
        $this->db->select($this->table, ["id" => $id]);
        if (!$row = $this->db->fetch()) {
            return null;
        }
        return $this->loadUser($row);
    }
    
    public function insert(UserInterface $user) {
        return $this->db->insert($this->table, [
            "name"  => $user->getName(), 
            "email" => $user->getEmail()
        ]);
    }
    
    public function update(UserInterface $user) {
        return $this->db->update($this->table, [
            "name"  => $user->getName(), 
            "email" => $user->getEmail()
        ], 
        "id = {$user->getId()}");
    }
    
    public function delete($id) {
        if ($id instanceof UserInterface) {
            $id = $id->getId();
        }
        return $this->db->delete($this->table, "id = $id");
    }
    
    private function loadUser(array $row) {
        $user = new User($row["name"], $row["email"]);
        $user->setId($row["id"]);
        return $user;
    }
}

Looking at the mapper’s contract, it’s easy to see how nice the CRUD operations that polluted the User class’ ecosystem before have been placed inside a cohesive set which is now part of the raw infrastructure instead of the domain layer. This single modification should let us refactor the domain class and turn it into a cleaner, more distilled structure that conforms to the principle:

<?php
namespace Model;

interface UserInterface
{
    public function setId($id);
    public function getId();

    public function setName($name);
    public function getName();

    public function setEmail($email);
    public function getEmail();
    public function getGravatar();
}
<?php
namespace Model;

class User implements UserInterface
{
    private $id;
    private $name;
    private $email;

    public function __construct($name, $email) {
        $this->setName($name);
        $this->setEmail($email);
    }
    
    public function setId($id) {
        if ($this->id !== null) {
            throw new BadMethodCallException(
                "The user ID has been set already.");
        }
        if (!is_int($id) || $id < 1) {
            throw new InvalidArgumentException(
                "The user ID is invalid.");
        }
        $this->id = $id;
        return $this;
    }
    
    public function getId() {
        return $this->id;
    }
    
    public function setName($name) {
        if (strlen($name) < 2 || strlen($name) > 30) {
            throw new InvalidArgumentException(
                "The user name is invalid.");
        }
        $this->name = $name;
        return $this;
    }
    
    public function getName() {
        return $this->name;
    }

    public function setEmail($email) {
        if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
            throw new InvalidArgumentException(
                "The user email is invalid.");
        }
        $this->email = $email;
        return $this;
    }
    
    public function getEmail() {
        return $this->email;
    }
     
    public function getGravatar($size = 70, $default = "monsterid") {
        return "http://www.gravatar.com/avatar/" .
            md5(strtolower($this->email)) .
            "?s=" . (integer) $size .
            "&d=" . urlencode($default) .
            "&r=G";
    }
}

It could be said that User now has a better designed implementation as the batch of operations it performs not only are pure domain logic, but they’re semantically bound to each other. In Single Responsibility Principle parlance, the class has only one well-defined responsibility which is exclusively and intimately related to handling user data. No more, no less.

Of course, the example would look half-backed if I don’t show you how to get the mapper doing all the data access stuff while keeping the User class persistence agnostic:

<?php
$db = new PdoAdapter("mysql:dbname=test", "myusername",
    "mypassword");
$userMapper = new UserMapper($db);

// Display user data
$user = $userMapper->findById(1);
echo $user->getName() . ' ' . $user->getEmail() .
    '<img src="' . $user->getGravatar() . '">';

// Insert a new user
$user = new User("John Doe", "john@example.com");
$userMapper->insert($user);

// Update a user
$user = $userMapper->findById(2);
$user->setName("Jack");
$userMapper->update($user);

// Delete a user    
$userMapper->delete(3);

While the example is unquestionably trivial, it does show pretty clearly how the fact of having delegated the responsibility for executing the CRUD operations to the data mapper permits us to deal with user objects whose sole area of concern is to handle exclusively domain logic. At this point, the objects’ tasks are principle-compliant, nicely distilled, and narrowed to setting/retrieving user data and rendering the associated gravatars instead of being focused additionally on persisting that data in the storage.

Closing Remarks

Perhaps just a biased opinion based on my own experience as developer (so take it at face value), I’d dare to say the Single Responsibility Principle’s worst curse and certainly the reason why it’s so blatantly ignored in practice is the pragmatism of reality. Obviously it’s a lot easier “to get the job done” and struggle with tight deadlines by blindly assigning a bunch of roles to a class, without thinking if they’re semantically related to each other.

Even in enterprise environments, where the use of contracts for outlining explicitly the behavior of application components isn’t just a luxury but a must, it’s pretty difficult to figure out how to group together cohesively a set of operations. Even though, it’s doesn’t hurt to take some time and design cleaner classes that don’t mix up unnecessarily heaps of unrelated responsibilities. In that sense, the principle is just a guideline that will assist you in the process, but certainly a very valuable one.

Image via Fotolia

Sponsors