The Single Responsibility Principle

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

Free book: Jump Start HTML5 Basics

Grab a free copy of one our latest ebooks! Packed with hints and tips on HTML5's most powerful new features.

  • j0k3r

    great article, thanks.
    a little typo in here:
    $userMapper = new UserMapper($adapter);
    $adapter should be $db, i think.

    • http://zaemis.blogspot.com Timothy Boronczyk

      Woops! Good catch, j0k3r. I’ve fixed it in the sample. Thanks!

    • Alex Gervasio

      Nice to hear that :). And thanks Tim for fixing up the typo.

  • lingtalfi

    I love it! So next step is to train to detect those responsibilities…

    • Alex Gervasio

      Hey,
      Glad you liked the post. Yes, sometimes it’s somewhat hard to outline the set of responsibilities that should be given to a class, even for experienced developers. As with many other OOP related things, experience in this case a great teacher. Thanks for the feedback :)

  • Mark

    Gervasio, gracias por los artículos. Personalmente los considero los más útiles que encontre para entender la forma de pensar en OOP; que al día de hoy me cuesta implementar correctamente.

    • Alex Gervasio

      Hey Mark,
      Sinceramente me alegro que mis artículos sobre OOP hayan sido útiles para ti. Gracias por los comentarios. Honestly, I’m glad to see my articles have been useful to you. Thanks for the comments.

  • http://www.maltblue.com Matthew Setter

    Hey Alex,
    Nice article. You hit the nail on the proverbial head when you mentioned pragmatism. I think the saying is: the road to hell is lined with good intentions. It’s not to say that we shouldn’t have a goal or aim to work towards. From time to time, it’s not always easy to hold to the ideal, for a variety of reasons. But it’s far better to know a rule and occasionally break it, than not know it in the first place. Thanks for covering this approach.
    Best,
    Matt

    • Alex Gervasio

      Hey Matt,
      Thanks for the comments. I totally agree with your opinion, I mean all the bunch of programming heuristics available out there unquestionably will let you write better, more robust code. However, when you have to deal with the ins and outs of the “real world”, sometimes sticking to them may be a pretty hard goal to achieve (not to say an utopian thing). I guess that in most cases exploiting the benefits provided by the heuristics in question, without being just a blind worshiper of them can be the proper path to pick up. Thanks again for the insightful comments.

  • Patrick

    I absolutely agree that SRP is often ignored for reasons of practicality and ease of development. That’s why I was quite disappointed by this article in the end. You took ActiveRecord as an example – one of the most widespread and obvious violators of the principle – but although you proposed data mappers as an alternative, you failed to explain why data mappers are superior in the real world.

    You provide nice code examples, but you haven’t addressed the fundamental issue and convinced me not to use ActiveRecord (for example). I do use data mappers in my own code, but if I was a user of ActiveRecord this article would not compel me to change. It’s not enough to just say “here’s an OOP principle you should follow” without explaining why the principle is helpful, with real world examples. What real problem does SRP solve?

    You have to acknowledge that ActiveRecord has become popular for a reason – it works. It’s easy to use and allows you to write code very quickly. Yeah, it violates SRP, but who cares if it gets the job done? That’s the question you failed to answer here. I was expecting you to give some concrete examples of how violating SRP can lead to real-world problems, but instead we get bogged down in endless code examples which fail to make a convincing case for developers to change their habits.

    I’d love to see a follow-up or amendment to this article elaborating on the benefits of the SRP (and the reasons something like ActiveRecord may cause problems), using real-world examples. Your articles are normally excellent, but I don’t think this one is particularly effective.

    • Alex Gervasio

      I’m not saying that AR is a bag thing at all, at least for small/medium projects. It’s practical, pretty easy to set up, specially if you’re using a framework which provides right out the box a bunch of prepackaged functionality. Unfortunately, because of its intrinsic nature, it deals with two different responsibilities, which ARE NOT semantically related to each other: data access and business logic, hence violating the SRP. It’s really that simple.
      On the flip side, data mappers are not the panacea neither. Even though, IMO they’re great for separating clearly the concerns of classes, at least when working with rich domain models, which by definition should be persistence agnostic. It’s in large, multi-tiered projects when you’ll see the actual benefits of data mappers over AR. It’s a lot easier to have multiple layers evolving at different paces (and even written and maintained by different developers) without interfering with each other. That sole reason, at least to me, its good enough for picking up data mappers over AR. In typical AR implementations, because the model (the so-called database model) is so closely tied up to the database, it’s hard to modify its business logic without rippling an explosion of effects to the methods responsible for handling data access. Not to mention that porting your model to another framework is simply a nightmare.
      I guess that should answer most of your questions. Thanks for the comments.

  • Stephane Deuvaert

    This is a great straight-foward article. My only concern is with your User class instantiation within UserMapper::loadUser. Maybe I’m confused, but I thought class instantiation should ideally never happen inside another class like that.

    • Alex Gervasio

      Hey Stephane,
      Glad to know the article has been instructive. Regarding your concern on where the User class is instantiated, there’re several approaches to take, each one exposing their own pros and cons. In this case in particular, where there’s a mapping layer keeping the domain model separated from the data access layer, the data mapper is certainly a good place to do the instantiation of the domain class, even if the mapper knows too much about the internal structure of the class in question. Making the mappers responsible for reconstituting back domain objects is a pretty ubiquitous practice, which allows to keep object construction separated from application logic. Thanks for the comments.

  • Uncle Fred

    Let me join this butt whooping party since everyone’s at it. On a second thought, why should I? While picking active record pattern abuse as a whooping boy was an easy target, the article still gets 15 out of 10 points from me since; SRP of SOLID was demonstrated clearly and with real-life code examples plus a plausible solution was also suggested.
    As you said its not easy to stay absolutely sterile in real life and maybe its not even necessary since its almost certain that version 1.0 will be followed with v1.1 where most of technical debt can be paid off while new features creap in and possible de-normalisation takes place. When I say most I don’t expect active record pattern to ever disappear since we have to work with framework tight coupled people and the so common loud mouth superstar ninja developer a.k.a. the incompetent.

    While people bash you on “how it should be done” I’ll tell you my story:
    Some time ago when I was still young and good looking :) Fresh out of UNI with my CS degree and goal in life to solve all worlds problems (by that time words “high cohesion and loose coupling” was still ringing in my ears). Imagine my excitement when in my first work place I was given a task to refactor four highly coupled god classes! Two days later I was ready to receive my praises. I still remember the look on my mentors face after I proposed my ultimate 27 class solution. I learned to lighten up my refactoring and perfectionism OCD when I was asked to loose 20 classes from my class diagram. It was like picking my favourite children and sending the rest queuing at the pearly gates.

    “monsterid” :) too funny! :D
    A++ would read again.

    • Alex Gervasio

      Hey Fred,
      As usual, it’s really nice to hear from you again. And even when my words may sound like a cheap cliché, yep, I’m glad to know you found the reading to be overall instructive (at least that was my humble purpose from the beginning). And don’t you worry: your story at your first work isn’t the only one out there. I personally went through similar situations, not only when I used to be fresh and young, but in recent times as well. By the way, A++? Well, finally I passed the test :). Thanks again for the insightful comments.

  • http://www.mariovaccari.com Mario

    Let me say that I totally disagree with this article.
    First of all, what’s wrong with having different responsibilities in a class given that you’re using PHP that is an interpreted language?
    Changing the implementation of a method will be transparent to every piece of software using the class, provided that the interface (note that interface here is used in a broad sense, that is everything a class expose as public) is not changed.
    There is no difference in putting part of the interface/implementation in a different class.
    Second point is that you strt with a class that is using getters/setters, even if you call them mutators/accessors. This is IMHO a bad choice from the start, while it’s questionable if getters/setters are evil they are something that must used with care, expecially with some real world objects that are more complex than what you used in your example.
    Given that you don’t have getters/setters, or you shoul preferably avoid them, it’s far from trivial make any object completely agnostic about the storage manager… or, it can be done, but it requires much more effort than you put in your example.
    Regards!

    • Antonio Carvalho

      Mario, it’s clear from your comment that you never worked on a large-scale project, but for those of us who did, it’s extremely important to respect and understand the pillars of good OOP design, or we risk losing the battle against complexity.

      Take the time to read more about SOLID and OOP in general and you’ll start to appreciate the wisdom shared by Alejandro on posts like this one.