Have you encountered a codebase where a single class/module doing so many things and any small change in a module breaks another and you think to yourself how this happened, if only you knew how to prevent this in the first place? Or a case when a senior developer reviews your PR and says move this to that class or a new class and you know that makes sense. How he/she decided that what rules are they are following. Don’t worry Uncle Bob to the rescue.
SOLID principles is a five principle guide if followed properly you can write software that will be less coupled, easy to understand, adding new features becomes easy, and will be easy to maintain. These principles are introduced by Robert Martin aka Uncle Bob
Let’s see the principle one by one.
Single Responsibility Principle
This is a simple principle and easy to understand.
Your module/class should have only one reason to change.
So what this means? This means your class should have only one Responsibility that is to serve a set of users. This is like one-to-one mapping between the class and the set of users. Any feature request from these users makes this class change. Any request feature request from other users should not make this class change, instead, create a new class that calls that class reuse some code.
This helps us to reduce the phenomena of breaking another module because changes are done in one.
Open Closed Principle
Standard definition: A class should be open for extension and closed to modification.
The goal is to make new features easy to accommodate. Whenever we have to add new features, the codebase should be in such a way that this can be achieved without modifying the current code. This principle is easy to understand by example, one example will be Factory Method Design Pattern.
Linksov Substitution Principle.
Standard Definition: That objects of a superclass shall be replaceable with objects of its subclasses without breaking the application.
So what this means, if you are deriving a class the behavior of the derived class should be the same as the parent class. Now you will be thinking that if a class is derived from the parent class, the behavior will be the same. Not always true.
“In mathematics, a Square is a Rectangle. Indeed it is a specialization of a rectangle. The “is a” makes you want to model this with inheritance. However if in code you made Square derive from Rectangle, then a Square should be used anywhere you expect a Rectangle. This makes for some strange behavior.
Imagine you had SetWidth and SetHeight methods on your Rectangle base class; this seems perfectly logical. However, if your Rectangle reference pointed to a Square, then SetWidth and SetHeight don’t make sense because setting one would change the other to match it. In this case, Square fails the Liskov Substitution Test with Rectangle and the abstraction of having Square inherit from Rectangle is a bad one.”
Interface Segregation Principle.
Definition: No client should be forced to depend on methods it does not use.
In short, interfaces should not be huge. If that’s case splits the interface into smaller interfaces.
If you have an interface that has 10 methods, and the client is going to use 1-2 methods. This is the indication there is a need for splitting.
Dependency Inversion Principle.
- High-level modules should not depend on low-level modules. Both should depend on abstractions (e.g., interfaces).
- Abstractions should not depend on details. Details (concrete implementations) should depend on abstractions.