The Single Responsibility Principle represents the “S” in SOLID. It means that a software module should only have one responsibility – in other words, there should never be more than one reason to modify that module (excluding, of course, refactoring and bug fixes). The responsibility of a module should be entirely encapsulated within that module and all services within it should be narrowly aligned with it.
This principle is closely tied to another one, namely the Separation of Concerns – separating an application into distinct sections, so that each of them addresses a discrete concern. You may also know it as modularity. A well designed module does only one thing, that is to say it has a single responsibility.
However, while the concept of dividing software into modules with separate obligations is familiar to most, many tend to ignore the importance of applying this principle to classes, and even methods.
How to define responsibility
Two definitions can help us find out how to establish the responsibilities of our module:
- Responsibility – a family of functions that serves one particular actor
- An actor for a responsibility – the single source of change for that responsibility
It stands to reason, then, that to find our module’s responsibilities, we must find the actors involved.
Example #1
How it’s broken
Let’s say we have a Planet and we want to print its name to the screen:
public class Planet { private String name; private int age; public Planet(String name, int age) { this.name = name; this.age = age; } public void incrementAge() { age++; } public void describe() { System.out.println("The planet " + name + " is " + age + " years old"); } }
public class SingleResponsibilityExample1 { public static void main(String[] args) { Planet alphaCentauri = new Planet("Alpha Centauri", 1000); alphaCentauri.incrementAge(); alphaCentauri.describe(); } }
It may not be immediately obvious, but we are already breaking SRP here. Right now, two actors are involved as the source of change for Planet – Nature and Observer – with two reasons to modify the class:
- Nature – to change how the properties are managed (e.g. when we want to change how age is incremented)
- Observer – to change how to display the information (e.g. when we want to start displaying it on a web page instead of in the console)
This means that Planet has two responsibilities. We could define them as: managing its own properties and displaying information.
How to fix it
The fix is relatively simple – we must make Planet‘s describe() method return a String, taking away the responsibility of displaying information to the screen:
public class Planet { private String name; private int age; public Planet(String name, int age) { this.name = name; this.age = age; } public void incrementAge() { age++; } public String describe() { return "The planet " + name + " is " + age + " years old"; } }
public class SingleResponsibilityExample1 { public static void main(String[] args) { Planet alphaCentauri = new Planet("Alpha Centauri", 1000); alphaCentauri.incrementAge(); System.out.println(alphaCentauri.describe()); } }
Now Planet can even be reused elsewhere – we could display its information on an HTML page, as part of a JSON response or sent as an e-mail – all without needing to modify its code. Its sole duty is managing its own properties, it serves only one Actor (Nature), the Single Responsibility Principle is no longer violated.
Example #2
How it’s broken
Consider a ShipNavigator, originally responsible for calculating a Route for a spaceship. The Route was used by the Captain to steer the ship. At some point it was decided that the Chief Engineer needs to be aware of the estimated fuel consumption for the route, so an additional method was added to the class:
public class ShipNavigator { public Route calculateRoute() { // Route calculation happens here return new Route(); } public int calculateFuelConsumption(Route route) { // Fuel consumption is calculated here return 10; } }
public class Route { // The implementation details of this class are irrelevant }
Originally, ShipNavigator only had one source of change – the Captain. Now, it has two – the Captain (who might want to change how the Route is calculated) and the Engineer (who might want to change how fuel consumption is estimated). Therefore, it now has two responsibilities.
How to fix it
As the calculateFuelConsumption() method is not related to ShipNavigator‘s original responsibility of navigating, we should extract it to its own class, the FuelEstimator:
public class ShipNavigator { public Route calculateRoute() { // Route calculation happens here return new Route(); } }
public class FuelEstimator { public int calculateFuelConsumption(Route route) { // Fuel consumption is calculated here return 10; } }
ShipNavigator once again only has one reason to change, as does FuelEstimator. All is well with the world, we are once again adhering to SRP.
Summary
To follow the Single Responsibility Principle, make sure that your modules, classes and methods have a single, clearly defined responsibility. Whenever you feel like a module is doing two or more things (or when you’re tempted to add “and” to your method name), consider decomposing it into smaller, independent sections. This will limit the amount of unrelated things that could break when you change the module and make your code more readable. If you have trouble defining responsibility, look for Actors which your module serves – and make sure it’s never more than one.
Photo by Suzy Hazelwood
[…] segregating interfaces ties well into the Single Responsibility Principle, as well as Separation of […]