SOLID Principles for Maintainable Code
The SOLID principles were developed by Robert C. Martin in a 2000 essay, “Design Principles and Design Patterns”, although the acronym was coined later by Michael Feathers. In his essay, Martin acknowledged that successful software will change and develop. As it changes, it becomes increasingly complex. Without good design principles, Martin warns that software becomes rigid, fragile, immobile, and viscous. The SOLID principles were developed to combat these problematic design patterns.
While the principles come with many benefits, following the principles generally leads to writing longer and more complex code. This means that it can extend the design process and make development a little more difficult. However, this extra time and effort is well worth it because it makes software so much easier to maintain, test, and extend.
1. Single Responsibility Principle
A class should have only one job.
Every component of your code (in general a class, but also a function) should have one and only one responsibility. As a consequence of that, there should be only a reason to change it.
When starting to code, too often we are tempted to write code that takes care of an entire process all at once. I.e., A function that handles requests, loads data, modifies and, plots them, all before returning its result. This is exactly what this principle tries to avoid.
By applying this concept it will be easier to localize errors. Any error in execution will point out to a smaller section of your code, accelerating your debug phase. Another added benefit is improved testing for each function of your code.
PD. This principle does not mean that functions or classes do only one thing, rather they should stick to only concept only. Making many classes overly simple will result in making the code just as hard to read.
2. Open-Closed Principle
Your code should be open for extension, but closed for modification.
Existing, well-tested classes will need to be modified when something needs to be added. Yet, changing classes can lead to problems or bugs. Instead of changing the class, you simply want to extend it.
- Open for extension, meaning that to accommodate a new functionality you can simply add what you now need.
- Closed for modification, meaning you should not need to modify the code you have already written.
It’s important to follow this principle in order to write code that is maintainable and revisable.
3. Liskov Substitution Principle
A sub-class must be substitutable for its super-class.
The principle defines that objects of a superclass shall be replaceable with objects of its subclasses without breaking the application. That requires the objects of your subclasses to behave in the same way as the objects of your superclass.
An overridden method of a subclass needs to accept the same input parameter values as the method of the superclass. That means you can implement less restrictive validation rules, but you are not allowed to enforce stricter ones in your subclass.
By following this principle, your code will be easier to maintain in the future, since modifications will not break your code and the behavior of your classes is standardized.
4. Interface Segregation Principle
Clients should not be forced to depend upon interfaces that they do not use.
This principle tell us that a class should only have the interface needed and avoid methods that won’t work or that have no reason to be part of that class. This problem arises, primarily, when, a subclass inherits methods from a base class that it does not need. This situation can lead to unexpected bugs if the methods get accidentally called.
In future, if new people start working on the project, doubts about the implementation of the missing methods can arise. Instead, engineers should work to have many client-specific interfaces and avoid the temptation of having one big, general-purpose interface. It might be faster and easier to implement only one interface, but in the long run the aim is to keep our classes as clean as possible and minimize mistakes.
5. Dependency Inversion Principle
Dependency should be on abstractions, not concretions.
The principle states that high-level modules should not depend on low-level modules; both should depend on abstractions. Abstractions should not depend on details. Details should depend upon abstractions. This principle is actually very tied together with the Open-Closed Principle, it is one of the ways that you can achieve to follow the second principle.
Code that violates this principle may become too coupled together, and that makes the code hard to maintain, unreadable, and prone to side effects.
Implementing SOLID design principles during development will lead to systems that are more maintainable, scalable, testable, and reusable. In the current environment, these principles are used globally by engineers. As a result, to create good code and to use design principles that are competitive while meeting industry standards, it’s essential to utilize these principles.