Allyx Gomes

Software Development Blog

The Single Responsibility Principle – Talking about S.O.L.I.D.

Keep it simple

Hello everybody! This is my first blog post, I stopped writing my articles a few years ago and noticed that I was losing an important part of writing: you learn a lot. So I’ll start rewriting some articles about S.O.L.I.D. and also finish talking about them with the examples rewritten in Ruby (the programming language I have been using). If you want to know more about me please the About page or my LinkedIn. Let’s start!

Before we get started, let’s quickly touch on what S.O.L.I.D. stands for. It’s a set of principles for good class design, which is one of the foundational standards for implementations across many languages and technologies we use. Understanding them is crucial for applying them effectively in the software you’re building. Here’s the list:

SRP – The Single Responsibility Principle: A class should have one, and only one, reason to change.

OCP – The Open Closed Principle: You should be able to extend the behavior of classes without modifying them.

LSP – The Liskov Substitution Principle: Subclasses should be substitutable for their superclasses.

ISP – The Interface Segregation Principle: Build interfaces that are specific to client needs, avoiding large and complex interfaces.

DIP – The Dependency Inversion Principle: Depend on abstractions, not on concrete implementations.

The first principle I’ll talk about is SRP, so let’s dive in.

Robert C. Martin (aka Uncle Bob), in his book Clean Code: A Handbook of Agile Software Craftsmanship, brings the following quotes:

No matter how beautiful a house is, a messy table takes away its splendor. He who is faithful in little is also faithful in much.

I hope that this quote can inspire you.

Definition:

A class should have one, and only one, reason to change. (Uncle Bob)

First example:

Let’s follow this line of reasoning: you want to format an attribute that stores the CPF (the unique personal identifier that every person should have in Brazil) in a class called Identification. The method format_cpf should return the CPF string formatted with dots and a dash. Check out the code below:

class Identification
  attr_accessor :cpf

  def initialize(cpf)
    @cpf = cpf
  end

  def format_cpf
    first_part = @cpf[0, 3]
    second_part = @cpf[3, 3]
    third_part = @cpf[6, 3]
    fourth_part = @cpf[9, 2]
    formatted_cpf = "#{first_part}.#{second_part}.#{third_part}-#{fourth_part}"
    @cpf = formatted_cpf
    formatted_cpf
  end
end

The usage of this method would result in the output below:

So, does the method do what it’s supposed to? Yes, but it does something more. Besides formatting the CPF, the attribute that holds this information is changed to the formatted version assigning the formatted value to our CPF attribute. Anyone calling this method will want just a formatted CPF string, but they will end up altering the CPF attribute every time they call format_cpf. Can you see the problem? This method doesn’t do just one thing, it does other things and could likely cause some additional future problems by making anyone who just wants text formatting also change an attribute without knowing.

Let’s move on to the second example, this one is taken from page 44 of the book Clean Code (rewritten in ruby):

class UserValidator
  def initialize(cryptographer)
    @cryptographer = cryptographer
  end

  def check_password(user_name, password)
    user = UserGateway.find_by_name(user_name)
    unless user.nil?
      coded_phrase = user.phrase_encoded_by_password
      phrase = @cryptographer.decrypt(coded_phrase, password)
      if phrase == "Valid Password"
        Session.initialize
        return true
      end
    end
    false
  end
end

The signature of this function makes it clear to anyone calling it that it is a check for login and password, verifying if the password is valid for the given user. The problem with this function lies in the Session.initialize part. Anyone calling this function with the intent of authenticating a user will always be starting a new session, which could lead to data loss. This is another dependency resulting from not applying SRP. Such code will confuse, and often users will start calling it with some restrictions, only when they ensure they want to both authenticate the user and start a new session. If this function cannot be separated for some reason, it would be ideal to rename it to check_password_and_initialize_session, as this, while still breaking the principle, at least makes it clear what the method does.

The SRP helps you maintain your methods, classes, and consequently, your project in a better way, as they become clear and small, which makes them easier to understand and reuse. I hope this content has given you an idea of how we should separate some of our implementations, and I hope this is just the beginning.

Let me know your thoughts about SRP if you agree/disagree, or if the standard is different in the programming language that you’re using in the comments!

This is the first of five articles on S.O.L.I.D., so see you soon! 😃

References:

Book: Clean Code: A Handbook of Agile Software Craftsmanship — Robert C. Martin

Article: http://butunclebob.com/ArticleS.UncleBob.PrinciplesOfOod

Leave a Reply

Your email address will not be published. Required fields are marked *