A brief introduction to S.O.L.I.D. principles
3 min read
S.O.L.I.D. principles are five well known software design principles for object-oriented programming, introduced by Robert C. Martin in the early 2000s. Despite their age, which it's like a century in software terms, they are still very used as they provide a framework for making software designs more understandable, flexible and maintainable.
Their name, SOLID, is a mnemonic that help us to remember each principle:
- S - Single Responsibility Principle
- O - Open-closed Principle
- L - Liskov Substitution Principle
- I - Interface Segregation Principle
- D - Dependency Inversion Principle
I'll briefly talk about all of them, for those who look for a quick introduction or just want to refresh their memory.
Single Responsibility Principle
Single-responsibility Principle (SRP) states:
A class should have one and only one reason to change, meaning that a class should have only one job.
The main goal of this principle is reducing complexity when your software constantly grows and changes. At some point, classes become so big that you can no longer remember their details, and code navigation becomes a problem as you have to scan through whole classes or even an entire program to find specific things.
And even more: if a class does too many things, you have to change it every time one of these things changes. While doing that, you’re risking breaking other parts of the class which you didn’t even intend to change.
Open-closed Principle
Classes should be open for extension but closed for modification.
Yep, this sounded confusing to me too, the first time I read about it. However, the main idea of this principle is to keep existing code from breaking when you implement new features.
A class is open if you can extend it, produce a subclass and do whatever you want with it (add new methods or fields, override base behavior, etc.). At the same time, the class is closed (you can also say complete) if it’s 100% ready to be used by other classes when its interface is clearly defined and won’t be changed in the future.
Liskov Substitution Principle
If class A is a subtype of class B, you should be able to replace B with A without disrupting the behavior of the program.
This means that a subclass should be substitutable for its parent class, which involves some restrictions when extending a class. For example, when overriding a method, it should extend the base behavior rather than replacing it with something else entirely. Also, returns types should be the same or a subtype of the original, and methods in a subclass shouldn’t throw types of exceptions which the base method isn’t expected to throw.
Interface Segregation Principle
A client should never be forced to implement an interface that it doesn’t use, or clients shouldn’t be forced to depend on methods they do not use.
Bog interfaces should be break into more granular and specific ones. Clients should implement only interfaces with the methods they really need. Remember that classes can implement multiple interfaces at the same time, hence there’s no need to agglutinate tons of unrelated methods to a single interface.
Dependency Inversion Principle
High-level classes shouldn’t depend on low-level classes. Both should depend on abstractions.
Usually when designing software, you can make a distinction between two levels of classes.
- Low-level classes implement basic operations such as connecting to a database, sending emails, uploading files to a server, etc.
- High-level classes contain complex business logic that directs low-level classes to do something.
High-level classes should import interfaces that define these basic actions, and low-level classes should implement these interfaces.