SOLID Principles (Series) [PART 1]

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...