This is part of my SOLID Software Principles in Practice post.


SRP states that every module, class, or function should have responsibility over a single part of the functionality provided by the software, and that responsibility should be entirely encapsulated by the class. All its services should be narrowly aligned with that responsibility.

That’s a lot to take in, so I prefer Robert C. Martin’s expression of the principle which is that “A class or function should only have one reason to change.”

Class-level SRP

Consider the following class:

Single Responsibility Principle Broken

Here we have a single Invader class, which has three responsibilities:

  • modify health each time the Invader is hit
  • move Invader
  • render Invader

Now, say that we decide that Invaders need to move in a different way, there is one reason to change. Or, say that we have written the game using DirectX for rendering and now we want to use OpenGL, there is another reason for change. We have broken the SRP principle and introduced excessive coupling.

To fix it we need to extract the two methods into their own classes:

Single Responsibility Principle Fixed

In this arrangement we have broken the excessive coupling. The Invader class only has a single reason to change and that is when we want to change the logic that governs calculating the health value each time an Invader is hit by a missile.


Method-level SRP

The previous example demonstrates applying SRP as the class level. SRP can/should also be applied at the function/method level. Consider the following code snippet:

public final class Game {
    private final Collection<Invader> invaders = new ArrayList<>();
    private final Collection<Missile> missiles = new ArrayList<>();
    private int score = 0;

    ...

    private boolean checkCollision(Invader invader, Missile missile) {
        if (invader.intersects(missile)) {
            score += 100;
            return true;
        } else {
            return false;
        }
    }

    public void mainLoop() {
        invaders.forEach(invader -> missiles.forEach(missile -> {
            if (checkCollision(invader, missile)) {
                // handle collision
            }
        }));
    }
}

checkCollision() is violating SRP, as it performs two tasks - checking for a collision and updating the score - therefore it has two reasons to change. Fixing this issue is trivial, we just need remove the unnecessary coupling and update the score outside of checkCollision() :

...

private boolean checkCollision(Invader invader, Missile missile) {
     return invader.intersects(missile);
}

public void mainLoop() {
    invaders.forEach(invader -> missiles.forEach(missile -> {
        if (checkCollision(invader, missile)) {
            // handle collision
            score += 100;
        }
    }));
}
...