SOLID Development: Single Responsibility Principle


In this section of understanding the SOLID development pattern we are going to walk through the single responsibility principle. While the concept of single responsibility has been around for a while it was popularized in 2003 by Uncle Bob.

single responsibility principle

Single Responsibility Principle

So what exactly is the single responsibility principle? The term is pretty self descriptive. Essentially it means that each class and module in a program should focus on a single task.

SRP in the Real World

single responsibility principle

Before we go into a code example, let’s look at the real world case study of a vacuum cleaner. A vacuum cleaner can perform a number of tasks such as cleaning floors and upholstery. Some of the more upscale vacuums can even work like a blower. However notice how, at its core, a vacuum has an engine that utilizes air to perform the task of cleaning?

It wouldn’t make sense for a vacuum cleaner to also wash windows. If you introduced this type of feature to the vacuum it may work for a while, but would most likely cause increased maintenance costs when it would inevitably break down.

Single Responsibility Principle Example in Ruby

Now that you have an idea of how the single responsibility principle works in the real world, let’s dive into a code example.

The Class That Knew Too Much

single responsibility principle

Here we have an Invoice class that seems to be relatively straightforward. The class:

  • Prints out details about the invoice
  • Calculates sales tax
  • Emails the invoice with its details

When we run the code everything works fine.

single responsibility principle

This may seem fine at first, however this code is breaking the single responsibility principle in a number of ways.

Rule of Thumb: No ‘Ands’ Allowed

When it comes to following the SOLID design pattern a good rule of thumb is that if your description of a class has the word and in it, then it may need to be refactored. For example, let’s describe this class:

The invoice class prints out invoice details AND calculates sales tax AND emails the invoice.

Whenever I’m performing a refactor I like to treat the behavior between the ANDs as their own class.

A Mailer Class

single responsibility principle

So let’s refactor this code, starting with the email feature. Here we have created a new class called Mailer that has a single method that will send out an email. The email method takes an argument where we can pass the invoice details to it. If we run this code we’ll see this is working properly.

single responsibility principle

Sales Tax Class

single responsibility principle

Next is our sales tax feature. This component definitely shouldn’t be included in the Invoice class since it doesn’t take much imagination to realize that this feature may be required by other parts of an application outside of the invoice.

Here we’re create a SalesTax class that takes in the state we want to generate the sales tax for. Running this code will show that our program is still working perfectly.

single responsibility principle

And now our Invoice class is following the single responsibility principle. Notice how the invoice is no longer in charge of generating sales tax or emailing customers?

Why the Single Responsibility Principle is Important

So why is this type of design pattern important? Our initial Invoice code worked fine, so why would we have to change it? Let’s imagine that this program was used by a real world accounting division. What if a user wanted to see what the tax rate would be for a specific state? It wouldn’t make sense for the system to require the user to create an invoice to calculate that value.

By refactoring the program in the way we did in this guide our SalesTax class could be used independently or by any other classes that may need the feature. Such as a project estimator class. In the computer science world this concept is called coupling. In our initial code example, the sales tax component was highly coupled to the Invoice class. This means that it would be messy to work with the tax rate generator without having to also work with the Invoice class.

Our refactor fixed this issue and now we can say that the tax feature has low coupling. Which means that users can access the tax rate component without having to work with other classes.


I hope that this has been a helpful guide to understanding the SOLID element of the single responsibility principle. And good luck with the coding!



Please enter your comment!
Please enter your name here