SOLID-ify Your Code With SOLID Design Principles
What are SOLID Design Principles?
SOLID is a set of governing principles for software engineers to adhere to create consistent, reliable, maintainable and expandable software. They were created 20 years ago by Robert Martin (but he goes by Uncle Bob if you ask us).
They exist as a universal law between developers which they should apply to all their work.
The SOLID principles are as follows:
Single Responsibility Principle
Liskov Substitution Principle
Interface Segregation Principle
Dependency Inversion Principle
As we said, developers follow these principles to produce better code, but there are other reasons behind SOLID.
First, you can never ensure the same developer will work on a software project forever. The chances are, someone else will have to pick up the code further down the line. Having these overarching principles acts as common ground for devs. Furthermore, if a software project scales and a host of new developers must start working on the code then the SOLID principles are crucial.
Finally, even if you managed to keep a whole team of software developers together, you would always have to iterate. SOLID design principles are essential to avoid spaghetti code and projects that are unmaintainable.
To summarise, the reasons to use SOLID principles are:
Well-written code using SOLID will help you extend your code in a sustainable, manageable and efficient way.
They allow a codebase to be worked on by many developers and teams. Therefore, limiting the possibility that an individual or new team will introduce bugs.
They improve overall coordination and help you avoid spaghetti code.
Enables you to approach a project, new or existing, and maintain a level of consistency.
They help you to stay clear of poor practice and ‘hacky’ solutions that will jeopardise your code further down the line.
Single Responsibility Principle (SRP)
The single responsibility principle dictates that an entity (or class) should only be responsible for one purpose. No more. No less. If a class is responsible for several functions it can lead to two potential outcomes (or even both):
A) Extremely difficult to know where the problem originates.
B) Likely to destabilise the rest of the code and introduce bugs.
The SRP solves the issue of class complexity by making sure one class is doing one thing only. Simpler classes, modules and functions are easy to understand, reason with, and amend in the future; increasing project longevity. They allow developers to effectively isolate bugs and issues and assess the impact any changes will have.
Open-Closed Principle (OCP)
A preexisting class should be not be changed to create an extension. Instead, developers should write it in a way that allows extension; without interfering with any existing implementations. That’s not to say a class cannot be derived from it. It should be designed with the future in mind and, therefore, open to extension. However, it should not be modified to make such an extension. Doing so would violate the OCP.
The OCP helps stop the possibility of problems further down the line when you want to modify or extend code; it becomes difficult to unravel the code to build in the new feature. The OCP aims to create more maintainable code. Furthermore, by violating the OCP, you could be opening your software to potential security threats by unnecessarily leaking information at critical points.
Liskov Substitution Principle (LSP)
The Liskov Principle, created by Barbra Liskov dictates:
Let p(x) be a property provable about objects x of type T. Then p(y) should be true for objects y of type S where S is a subtype of T.
Essentially, if a parent class is ‘T’ and it can perform a function, then the derived class should execute the same function, without breaking the application.
In software engineering, derived classes should be used in places where its base class is used and program correctness should be maintained.
Interface Segregation Principle (ISP)
When building an application, you have to be wary of interface size. If you were to create one big interface, any changes to that interface would have a larger than necessary impact. Therefore, requiring all implementations to be updated whether or not a function is used.
Alternatively, you can break down lots of smaller interfaces that define the unique requirements of what each interface needs. Implementing smaller interfaces means your classes or modules only define the behaviour they need to achieve their purpose.
Interface segregation is advantageous, because much like the SRP, it keeps your code self-contained, only performing what's necessary. In turn, it reduces the chance of bugs and the application breaking down. In addition, having many individual interfaces makes them more reusable in the future and reduces the impact of prospective changes.
Dependency Inversion Principle (DIP)
This 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 on abstractions.
DIP splits the dependency between the high-level and low-level modules by introducing an abstraction between them. So in the end, you get two dependencies; the high-level module depending on the same abstraction as the low-level.
Overall, the DIP principle aims at reducing the dependency of a high-level Class on the low-level Class by introducing an interface. DIP increases the maintainability of your codebase by isolating your dependencies. Subsequently, any changes to that dependency can be focused in a single place without making large sweeping changes, when they can be carelessly missed.
Should the dependency you require become obsolete, you can replace or adapt a new dependency to fit into your application.
Why we use SOLID Design principles
The real question is then, why do we need to use the SOLID principles?
Well, the answer is, you don’t. Nothing can stop you from taking shortcuts and hacking fixes together, but something tells me you’re not here for that.
The root cause of so much buggy software is usually down to time and money. These barriers hinder a developer from writing code that is well-designed and will be maintainable. The lack of time spent leads to corners being cut and inevitable technical debt. Not great.
The other factor is lack of planning and for lack of a better word, competence. So many developers can rush into a project and not plan for the twists and turns of a software project. The SOLID principles are there to improve your code, keeping it consistent, reliable, maintainable and expandable.
Sign up to our newsletter
Be the first to hear about our events, industry insights and what’s going on at Komodo. We promise we’ll respect your inbox and only send you stuff we’d actually read ourselves.