Have an overview of SOLID, and now I going to tell you about each one of the principles that form part of SOLID.
Taking as a referece the book “Clean Architecture” by Robert C. Martin we can say the following:
Good software system being with clean code. On the one hand, if the bricks aren’t well made, the architecture of the building doesn't matter much. On the other hand, you can make a substantial mess with well-made brick. This is where the SOLID principles come in.
The SOLID principles tell us how to arrange our functions and data, and the goal of the principles is the creation of mind-level software structures that:
Well, now we have an overview of SOLID, and now I going to tell you about each one of the principles that form part of SOLID, those are the following:
“A module should have one, and only one reason to change”
“A software artifact should be open for extension but closed for modifications”
“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”
“Clients should not be forced to depend on methods they don't use”
“High-level modules should not depend on low-level modules. Both should depend on abstractions. Abstractions should not depend on details. Details should not depend on abstractions”
We going to take base on each one of the principles in the next section.
Note: For more information about SOLID I recomend to read the book “Clean Architecture” by Robert C. Martin
As we can see in the previous section, SOLID is thought for Object-Oriented programming but that is not a limitation for languages like Golang and I will show you how you can arrange it and implement it in your Golang projects.
Let us start with each one of the principles and define how are implemented with Golang.
— Definition: “A module should have one, and only one reason to change”
We already know what the principle means, now is time to learn how to implement it in Golang.
For golang we could define this priciple as “One function or type should have one and only one job, and one and only one responsibility”, Telling that is time to go ahead with an example to see it in action:
Having the next code:
As we can see is not bad at all, but, what is happening in the code?. If we stop a moment and read the lines 14 and 23 inside of area methods we can discover something, the code is calculating the area and is printing the result, it is breaking the “single responsibility principle”.
Now, applying the “single responsibility principle”:
In line 11 we are passing as a parameter the shape interface, it helps us to use any type that implements the interface methods, in this case, square and circle types whos define the area method.
As we can see in the example the code defines the codes with separate responsibilities, in one hand the square and circle types define the method area with the action to calculate the area and return the value, on the other hand, the type outPrinter will define all the need to generate the needed string output.
— Definition: “A software artifact should be open for extension but closed for modifications”
Lets start with the following code:
The previous code doesn’t implement the “open-close principle“, the reason is the following:
How to fix it?
We can follow the next approach:
And here is the way to use the new implementation:
— Definition: “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”
Golang doesn’t have inheritance but has composition. we can compose multiple structs and it will work for our example of the “Liskov substitution principle”. Let's go to the code example:
In the code above we can analyze the following:
In a conclusion, we can say the following, the “Liskov substitution principle” is applied because the “car” type and “motorcycle” type could be substituted by the “vehicle” type.
In the following code, we can see the main function and the console output.
— Definition: “Clients should not be forced to depend on methods they don’t use”
Is simple as its definition, and we can say “Keep interfaces simple, preferable just one method”.
In the following example I’ll show you how to apply this principle in Golang:
Let us start, in the image below we can see defined an interface with two methods, one for the area and another one for the volume, all is ok until the moment.
Now, as we can observe in the image below the code defines two shapes, one square and one cube, each one implementing the method area() and volume().
And here we have two functions, one to sum the areas and another one to sum the volumes.
All looks good in the code, but if we analyze a bit and remember the principle “Clients should not be forced to depend on methods they don’t use” we can see the following thing in the shapes:
Based on those points we could fix the code by implementing the next changes:
In our example square implements “area()” and cube implements “area()” and “volume()” that covers the “interface segregation principle” because we are splitting the interfaces to don’t force them to use or define a method that won’t be used by the chape, in the example case the square.
And the last change, the functions to sum areas and volumes need to be changed to just accept the correct shapes.
— Definition: “High-level modules should not depend on low-level modules. Both should depend on abstractions. Abstractions should not depend on details. Details should not depend on abstractions”
Here is an example to explain more what the principle means.
In the image below we can see a very basic example that implements a “database” connection and a repository that is used to query the data.
The code doesn’t comply with the principle because it is defining in the repository the struct type to access the query methods.
Now, Implementing the “Dependency inversion” principle the code is modified in the next points:
In the image below we can see the changes applied based on the principle definition:
Note: We could improve the code using factories to initialize the repository properties.
As software developers, we should always look for the best way to write our codes, and applying the SOLID principles in our projects will help us write code more rigid, scalable, and tolerant to modifications since we are correctly defining the foundations of our application.
Join other developers and claim your FAUN account now!
Only registered users can post comments. Please, login or signup.