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