Skip links

SOLID - The S is for Single responsibility

23 March 2012 08:26 - by Freek Lijten - 9 comments

Tags: ,

SOLID is an acronym that stands for five different principles that have to do with clean object oriented code. Lately the term SOLID seems to get more widespread in the community of PHP developers. Talks are given, blogs are written and information is shared. What I'm missing in all these is a thorough explanation of why these principles matter. I see a lot of explanations of the principles, but the why is lacking. I will try to create a series showing the why of each of the five SOLID principles, starting with the S of Single responsibility.

What?

The single responsibility principle isn't all that hard to explain. It states that an object should do one thing, and one thing only. Or as Robert C. Martin (see props below) puts it:

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

He follows through with:

Why is it important to separate these two responsibilities into separate classes?
Because each responsibility is an axis of change

These two quotes are all there is to the single responsibility principle. A responsibility is a reason to change, and a class should only have one of those. Now all of this may sound abstract and since the objective of this series is to avoid just that we'll just dive into the why now.

Why?

Now for the fun part, code :)

Lets take a look at a concept called active record. In short these are smart models, they can create, read, update and delete themselves. In other words they represent a row in a database. This is a concept that is widely used inside and outside the PHP community. Code using active records tend to look like this.

$Bike = new Bike();
$Bike->wheels = 3; //this is a very special bike
$id = $Bike->save(); //create

$Bike = new Bike($id); //read
$Bike->wheels = 2; 
$Bike->save(); //update
$Bike->delete(); //delete

The save function will probably look somewhat like this:

public function save()
{
    //magically get DB-object
    if ( ! empty($this->id)) {
        //update DB
    } else {
        //create in DB
        $this->id = mysql_insert_id();
    }
    return $this->id;
}

Apart from that, the Bike class also does another thing, it is able to compare two bikes and determine if they are equal (the implementation is simple, you'll get the idea):

public function equalTo(Bike $Bike)
{
    return $this->wheels == $Bike->wheels;
}

The setup above, a model of some object that stores itself and has one (or more) convenient methods isn't all to uncommon. In fact it seems very convenient and doesn't look all that bad. It will be working more than fine. But it isn't what you want...

Reasons to change (multiple responsibilities)

You probably will have noticed the multiple responsibilities this class has, and thus the multiple reasons for changing the code:

  1. A change of datastore
  2. A change in the way bikes are compared
  3. A change in the properties a bike consists of (the only way you'd want for a model)

A clear violation of the Single responsiblity pattern as this class obviously does 3 things, it models a bikes, it stores a bike, and it compares bikes. But why should you care?

Problems

This approach results in several problems, I'll list them below.

Datastore lock-in

The model is now tied to the datastore. Easily exchanging MySQL with something else is not possible without changing this (and every other) active record. As we'll see later, this will give us problems while testing as well. It would of course be possible to inject a datastore object, but you'd still have to write the queries. If you'd also inject the way the object is saved to the datastore (queries) why bother with an active record at all? Might as well create some sort of repository for it.

Encourage other Single responsibities breaks

The datastore is now available inside the Bike class. Not only does this encourage developers to query the datastore for other reasons than CRUD (Create, Read, Update, Delete) inside the Bike class, but it also causes another problem.

Having the datastore available inside the Bike class implicitly encourages the direct use of the datastore in other places as well. You end up with the datastore being used all over the place, making the datastore lock-in even worse.

Too much information

This Bike will soon be an ObfuscatedBike. Already code for CRUD and comparing will be in the way of the definition of the bike and this will only grow. When you want to add a function to your code that merges two bikes for instance, what will be the obvious place?

In the current setup almost everyone would choose to add it to Bike. And you will for the next functionality too. And the one after that. These kind of classes tend to be hundreds of lines long, while it only started with a simple save function. I've read and wrote these classes and they're a pain, period.

Unit test problems

Let's assume we want to write a unit test for the compare function. Using PHPUnit, you would probably do something like this

require_once '/path/to/Bike.php';

class BikeTest extends PHPUnit_Framework_TestCase
{
    public function testBikesAreEqual()
    {
        $Bike1 = new Bike();
        $Bike1->wheels = 2;
        $Bike2 = new Bike();
        $Bike2->wheels = 2;

        $this->assertTrue($Bike1->equalTo($Bike2));
    }

    //not equal test be here
}

Seems ok enough right? But since the Bike class depends on the datastore this would probably result in some sort of fatal error.

  1. The database class may be autoloaded by your framework, but the framework is not loaded when PHPUnit executes.
  2. If the database class is required at the top of the Bike class it probably can't be constructed (missing configuration it would have when inside your framework for instance).
  3. I actually ran into the first to problems on occasion, but perhaps there are even more you can think of.

So now we must start fixing code while all we wanted to do was test the function that compares Bikes. A function that doesn't even need a datastore!

Now let's assume we would inject the datastore in the Bike class by passing a datastore to the constructor (or in every function that uses the database), then we would have to mock a datastore object to test the equalTo functions, or we'd have to write a hell of a lot of code each time to pass a datastore object to each function of the Bike using it.

I think by now you will start to see the problems with our Bike class, let's come up with a solution.

A better approach

We already know Bike has three responsibilities, so why don't we model our code to represent that:

class Bike
{
    public $wheels;

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

class BikeComparator
{
    public function bikesAreEqual(Bike $Bike1, Bike $Bike2)
    {
        return $Bike1->wheels == $Bike2->wheels;
    }
}

class BikeRepository
{
    public function save(Bike $Bike)
    {
        //do whatever
    }
}

So what have we reached here:

  1. A Bike is just a Bike, even with a type of steering wheel, amount of gears, brand name and price added anyone will be able to see what a Bike consists of at a glance. 
  2. The BikeComparator can be happily tested, it has no other Dependency than the Bike class itself which is perfectly valid, it is comparing Bikes after all. I can understand you might think this is silly, but keep in mind this is a simple example. Comparing multiple properties with different rules  will easily result in a lot of code.
  3. The BikeRepository does the saving for us. It's only responsibility is storing and retrieving Bikes.

You might think that BikeComparator still has two reasons to change, as does the Repository. Add a wheel size for instance and you'd have to update both classes. In case of the BikeComparator this isn't necessarily true. The comparator only needs to change if the new property matters while making a comparison. If you want to compare between types (trikes, normal bikes and a skelter of some sort) and don't care about wheel sizes, the BikeComparator needn't change at all. It is safe to say that there is still only one reason for change.

The repository is a slightly different case. It needs to change if the bike gains a property or if the datastore needs to change (MySQL -> NoSQL or something similar). You can solve this in multiple ways, but it is a little outside of the scope of this post. What you could do is introduce a Repository class that defines the properties and types that need to be saved and then load it with different classes that provide the actual implementation of a save action in MySQL, NoSQL, whateverSQL, etc. Also take a look at ORM systems like Doctrine for this kind of problems.

Conclusion

I hope by showing a very small (but perfectly imaginable) example how annoying even the smallest violations of the single responsibility pattern can be. As the person who came up with the term once said (freely quoted):

Of all the SOLID principles the single responsibility principle might be most easy to understand, but it is the hardest to get right.

I think I'll end this article with that encouragement :)

Props

Robert C. Martin is the father of the term SOLID. I've read several of his books and articles and he is a very skilled writer:

Share this post!

Comments

  1. LesLes Wrote on 23 March 2012 18:34

    I would disagree with you on the following point,

    it models a bikes, it stores a bike, and
    it compares bikes.

    There are not 3 things as you suggest but only one as the later two are actually part of the model responsibilities.

    What you have done is to consider them as separate responsibilities and there is no need to do that.

    Do not get me wrong as relatively simple logic like this it's quite acceptable to be left to the Record in question... however anything more complicated I would move elsewhere.

    I know others may (will) disagree but there is no need to complicate matters when there is no need to and certainly not until you need to.

  2. mrokmrok Wrote on 23 March 2012 19:00

    Great article. You explained it in a way easy to catch. I am waiting for other letters (sOLID).

  3. Freek LijtenFreek Lijten Wrote on 24 March 2012 09:54

    @Les:

    I understand what you're saying but I only partially agree with you. As far as I am concerned, a model should never perform CRUD operations on itself. Every time I wanted to write a test and had to use an active record it caused me problems.

    I can imagine one would leave the comparison function in the Bike function, but keep in mind this is a very small example due to space constraints :)

    A real Bike class and comparison would probably check more properties, take some rules into account and more. This will soon grow beyond a single function and you will be glad you separated it from the model at that point. I think it can be valid to leave such a small function in the model, but it is a slippery slope to more "pollution".

    @mrok: thanks, the others will follow, don't know at what pace though :D

  4. Thomas HoufekThomas Houfek Wrote on 25 March 2012 08:30

    IMO, Freek is right. It may seem unnecessarily complicated to divide the Bike object up into multiple objects. However, as soon as you need a major behavior to change, that all-in-one class is going to getvery unnecessarily complicated. It is not a violation of the YAGNI principle to do alittle extra work to prepare for the inevitable, unknowable, future needs.

    When writing OO code it pays to make more (and smaller) objects, each with a single responsibility.

    This can be hard to swallow, because your application seems to turn into something that has a lot more pieces, and this is a bit disorienting when you are used to organizing functionality differently. The original Bike object in this essay is like a flashlight-plus-screwdriver that you like because it is often very convenient. But eventually you will encounter a situation where you wish you could separate the flashlight and the screwdriver.

    Freek's example is meant to be simple and I am sure he would agree it is just a starting point. One continually finds compelling reasons to break classes up.

    For example, say I want want to transmit your Bike records to a remote data store via a web service. Wouldn't it be nice if my client code could easily choose whether to save the Bike to the local RDBMS or to the remote data store?

    Let's say I already has a lot of code written against those three objects Freek sketched. I could relocate most of the code in BikeRepository into a MysqlBikeMapper class which implements a BikeMapper interface with methods like load() and save(). Because I have all this code depending on the BikeRepository I add logic to the constructor dictating that by default the BikeRepository will use the BikeMysqlMapper to load and store Bikes:

    class BikeRepository 
    {
        protected $bikeMapper;

    public function __construct() { $this->bikeMapper = newMysqlBikeMapper(); } ..


    Then the BikeRepository drives the BikeMysqlMapper with methods like:

    public function save(Bike $bike) {
        return $this->bikeMapper->save($bike);
    }


    This may seem unnecessarily complicated, but stay with me. :) To add the ability to use a web service (let's say you are forced to use a SOAP service), you create a BikeSoapMapper class which also implements the BikeMapper interface. Then you can supply the old BikeRepository with a method giving new client code a way to switch mappers:

    public function useMapper(BikeMapper $mapper)
    {
        $this->bikeMapper = $mapper;
        return $this;
    }


    This preserves your existing dependencies and allows new code to:

    $soapMapper = new BikeSoapMapper;
    $repository = new Repository();
    $repository
       ->useMapper($soapMapper)
       ->save($bike);


    This is an oversimplified continuation of an oversimplified original example. Nothing in it tells you how the adapters get their differing connections or connection parameters, for instance. Hopefully it helps to support Freek's case for the Single Responsibility Principle, though. (Apologies for jumping ahead a little to the Interface Segregation Principle.)

    Yes, your program will have a lot more classes, but each class will have more integrity. Your classes can work together in a way that is more fluid and robust. Freek is also absolutely right that code adhering to this SOLID principle (and the other ones) is easier to test well.

    Should you immediately get religion and refuse to write code any other way? Probably not. You have a way of working that gets the job done, and your boss will expect jobs done long before you get comfortable with a new approach. But if you push yourself to code in this direction (and to write tests) I promise you will become a stronger programmer.

    Thank you Freek, for the great article.

  5. Wil Moore IIIWil Moore III Wrote on 25 March 2012 20:52

    What I think a lot of developers miss is that separation of concerns is not just a good idea for OO development...it is ALWAYS a good idea.

    How can you test a piece of code in insolation if that code is responsible for doing multiple things. The active record pattern is a great example of this not only because it violates the SRP, but also because it forces your domain model objects to inherit from objects that are not even related.

  6. Freek LijtenFreek Lijten Wrote on 26 March 2012 10:05

    @Thomas, thanks, that is a very clear addition which indeed will return full featured when I get at the I in SOLID :D

    @Wil, you are absolutely right, can't say anything against SRP in any style of coding. Thanks for your reaction

  7. AlexanderAlexander Wrote on 30 March 2012 14:52

    Freek, thx for the great article. It helps to get better understanding that such functionality as 'storing' and 'comparing' is actually not a standard set for a model, but really different separate responsibilities. I didn't see this fact and didn't understand SRP so deeply until I read you post. So, thx once again to share your knowledge =)

  8. gdudgdud Wrote on 12 February 2013 09:44

    Great explanation of this principle!

    I'll try put it in practice soon.

  9. intripintrip Wrote on 31 January 2014 23:44

    Here is a cool article about Single reponsability and PHP: http://www.jacopobeschi.com/post/solid-design-principles-single-responsability

Leave a comment!

Italic and bold

*This is italic*, and _so is this_.
**This is bold**, and __so is this__.

Links

This is a link to [Procurios](http://www.procurios.nl).

Lists

A bulleted list can be made with:
- Minus-signs,
+ Add-signs,
* Or an asterisk.

A numbered list can be made with:
1. List item number 1.
2. List item number 2.

Quote

The text below creates a quote:
> This is the first line.
> This is the second line.

Code

A text block with code can be created. Prefix a line with four spaces and a code-block will be made.