The SOLID principles tell us how to arrange our functions and data structures into classes, and how those classes should be interconnected. The use of the word “class” does not imply that these principles are applicable only to object-oriented software. A class is simply a coupled grouping of functions and data. Every software system has such groupings, whether they are called classes or not. The SOLID principles apply to those groupings.
The goal of the principles is the creation of mid-level software structures that:
Tolerate change,
Are easy to understand, and
Are the basis of components that can be used in many software systems.
I put the quotes from the book here. I don't want to change any theory. I think those quotes are clear enough.
What I want to show in this article is implementations. You can find implementations from other articles too. Most of them made an example for every principle. I also do the same, including a program that I made that applies all principles.
Shapes
A simple app of GoLang to calculate the area and perimeter of shapes that implement SOLID. I also made another version in TypeScript and PHP. Feel free to clone the repositories:
It looks simple. But exactly, it has two responsibilities; checking permission and adding shape to the user. We can put it to another function and make it more dynamic.
A software artifact should be open for extension but closed for modification.
Take a look at our program, Shapes. All of components such as Shape, User, and Repository are open for extension but closed for modification. Thanks to interface.
What is wanted here is something like the following substitution property: If for each object o1 of type S there is an object o2 of type T such that for all programs P defined in terms of T, the behavior of P is unchanged when o1 is substituted for o2 then S is a subtype of T.
That quote is from Barbara Liskov in 1988. You must be confused at the first time. That's why I made this article to bring an example of the implementation. Let's refactor our Shape to be more extendable using Liskov principle.
As we can see from examples above, setA(), setB(), area(), and perimeter() have their own details or implementation. Circle and Square only need setA(), but a for Circle is radius and for Square is side. NonEquilateral has setB() that Rectangle needs. Circle and Square don't need setB(). It will break the Liskov principle if they are forced to implement setB().
Interface Segregation
The lesson here is that depending on something that carries baggage that you don't need can cause you troubles that you didn't expect.
In our program, Shape, to get information of ShapePerimeters() and AverageShapePerimeter() are only for PremiumUser. If we only have one interface and even ShapePerimeters() and AverageShapePerimeter() return 0, it will break the Interface principle.
Dependency Inversion
The Dependency Inversion Principle (DIP) tells us that the most flexible systems are those in which source code dependencies refer only to abstractions, not to concretions.
The code below is a common example of the Dependency Inversion Principle.
Repository is also an Anti-Corruption Layer (ACL). On our code above, the Repository only has the Memory implementation. No matter how many its implementation at the future, they need to implement all methods from the Repository. Those implementation will be consistent depend on the Repository. If we call the implementation directly, it will break the Dependency Inversion Principle.
Closing
I hope this single article can make you understand about SOLID principles. SOLID is important. It is a must even since the developers begin their journeys. It is one of foundations to make good software.
All tabs of examples in this article are powered by SemanticTabs. It is a VanillaJS library written in TypeScript, super lightweight, only 1.09 kB, to make semantic tabs.
References
I want to thanks to Marko Milojevic and DigitalOcean that inspire me to write this article. But I still suggest you to read the write an original paper of SOLID from Uncle Bob that introduced these software design patterns.