SOLID Principles (Series) [PART 1]
SOLID - make Object-Oriented designs more understandable, flexible and maintainable.
If get to know something new by reading my articles, don't forget to endorse me on LinkedIn
What is SOLID?
SOLID Principles are an object-oriented approach that are applied to software structure design. SOLID principles were introduced by Robert C. Martin in his 2000 paper “Design Principles and Design Patterns.”
Why Design Principles matters?
Encourage us to create more maintainable, understandable, and flexible software.
As applications grow in size, we can reduce their complexity and save ourselves a lot of headaches.
Software is modular, easy to understand, debug, and refactor.
What SOLID stands for?
**S: **Single Responsibility Principle.
**O: **Open–Closed Principle.
**L: **Liskov Substitution Principle.
**I: **Interface Segregation Principle.
**D: **Dependency Inversion Principle.
In this series, we'll start by defining each principle and then some examples to help us understand how and why we should use these principles in our code.
SRP : Single Responsibility Principle
The SRP states that every Class
must perform a single functionality. Implementation of multiple functionalities in a single class mashup the code and if any modification is required may affect the whole class. It precise the code and the code can be easily maintained.
Example of SRP:
class User {
fun getFirstName(): String {...}
fun getLastName() : String {...}
fun sendEmail(content : EmailContent) : Boolean {...}
fun updatePrimayEmail(newEmail : String): Boolean {...}
fun sendSMS(content : SMSContent) : Boolean {...}
}
We have a class called User, which has some responsibilites getFirstName()
, getLastName()
, sendEmail(content : EmailContent)
, updatePrimayEmail(newEmail : String)
, sendSMS(content : SMSContent)
. Everything looks good and okay, right? Yes but only for this time only. Because it will lead to some challenges. We can not make the codes resuable. All responsiblites of this class is interconnected and that would be hard to fix any error if occurs. Also, as long as the code base grow, responsibilites added to the Object
, makes it harder to refactor, maintain, or fixing any issue.
Let's talk about Email
sending responsibilites. Currently, we have one API
called sendEmail(content:EmailContent)
which perform operation to send an email to a specific user. Lets say, User
might have secondary email address. Also, we might need an API
to re-send user verification email. If we add all of those APIs
one by to the User
object, object will get messy, un-maintainable, hard to refactor and if any issue occurs hard to fix.
So, how do we solve this problem? By following SRP.
We can move all of the email related APIs
to another service called EmailService
. Now, EmailService
is responsible to provide APIs
for email related business logic.
class EmailService {
fun sendEmail(user : User, content : EmailContent): Boolean {...}
fun resendVerificationEmail(user: User, content : ResendEmailContent) : Boolean {...}
}
For, mobile SMS related APIs
can be moved to SMSService
class SMSService{
fun sendSMS(user : User, content : SMSContent): Boolean {...}
}
So, our final User
class would be
class User {
fun getFirstName(): String {...}
fun getLastName() : String {...}
fun updatePrimayEmail(newEmail : String): Boolean {...}
}
Now, responsiblites of our User
object has been moved to respective services. Thus we can acheive SRP. Keep in mind that, this is just an basic example.
In next part, we will talk about Open–Closed Principle.
That's it for today. Happy Coding...