In our daily life, we have some problem statement and we want to solve it by hook or crook, once we fixed the problem then we left because it works fine right :) at least I am doing. We never think that solution can be sustained long term or will have any problems in the future. We do this way because of our whole attention toward problem-solving. Almost 90 percent of people do like this. Similarly in coding also get the task to perform with some base timeline. Our thinking just starts in a similar direction to solve,
- we solve the problem on paper.
- Write some pseudo code, algorithm kind of thing.
- We dry run with some test data. Once we feel ok we instantly go to the code editor and start implementation mostly in a procedure way. And once it did we are near to the timeline and we think we accomplish the task. Time pressure and problem solved satisfaction illusioned our mind that we are complete the task 100%.
But the most important thing is we have completed only 40% to 50% part of the problem in the programming world.
In this article, we will do an analysis for the remaining part of the completion of the problem in our hands.
Disclaimer: Solid principle covers 20 % of the problem completeness and the next 30 % is guarding the code with the test coverage and will not be able to achieve properly if we didn’t follow the solid principles.
Welcome to solid design principles.
We will learn all about solid principles. and believe me, your vision will be more clear while solving real-world problems.
Let's go through the following questions and you will get the answers to each while reading further.
- What are solid principles?
- Why should I care about it?
- How do we identify the violations.
- Advantages of solid design principles
- End result of following solid design principles
1. What are solid principles? To understand the solid design principle lest break it what is the into {Solid } {principles}
Solid
In general meaning of Solid means Stable, not breakable, something we can rely on and which will not easily break. In programming also consider the almost smilier meaning which makes your code rock solid, not breakable mean less buggy, and more flexible for future change with less effort.
Principles
Principles are those things that stand you proud in our own eyes. Everybody has their own principle to leave life. One of the principle I won’t take the loan in any case, I won't lie, a kind of thing which help to make ourself make stand solid. Similarly So there are some principles to make code rock solid so we called them SOLID principles.
Engineers are so clever they fit this principle in the SOLID name only :) How fascinating is it?
Solid is an Acronym for
- S -> Single responsibility principle
- O -> Open close principle
- L ->Listcko substation Principle
- I -> Interface segregation Priciple
- D -> Dependency inversion principle.**
Single responsibility principle
The single responsibility principle is saying that “The class should have only one reason to change and should have only one responsibility“ If we found multiple reasons then we are violating this principle.
example //image
List this above code and identify what is wrong with this code.
We identify this code we found that their many responsibilities are performing this code.
The main responsibly of the Employee Service class is to add employe on 10000 feet view.
Now Lest look very closely and list down the responsibilities
- Creating logger class object
- Validating input
- Creating database connection objects and saving them in the database
- Creating SMTP client object
- Sending mail.
Congratulations we successfully identified the additional dependencies and we are able to know there is an issue. You have to do the same once you complete your 50% of the problem statement. You can do this exercise to check are there multiple responsibilities in your code?. So you can identify the problems in your code.
Consequences of voilation of single responsibility principle.
- You are not able to write the test for this code means testing is difficult.
- If you find the bug in this code you have to do the whole testing for small change.
- Difficult to identify the problem if it has too many responsibilities
- In the future, any new change comes then it will default to integrate new changes, and again you need full 100% testing.
The solution to fix this problem is to create separate classes for each responsibility and inject this into the consumer class.
Now it's time to overcome this problem by simply introducing a separate class for each responsibility.
we have created a separate class for their responsibilities and injected the dependency in the EmployeeService class and now see the simplicity of class and we reach the highest abstraction level here
- Open close principle.
The open-close principle is saying that “Software component is open for extension but close for modification”
If we see the about code if any requirement comes in the future there is no room for extension. To satisfy thy e requirement we have to modify the existing code which is a violation of the Open close principle.
Let's take the example in the future we got the requirement that we have to add the validation while adding an employee so his designation is the manager So we have to modify the existing code. I hope you are connecting the dots if we violate the single responsibility then there is a high chance of violation of another principle also.
There is 2 solution to this problem. - By using inheritance - By using composition
Inheritance is less flexible and rigid. Their composition is very much flexible this is itself a big topic to discuss. But here is a short explanation
Suppose class A has method test () and a new requirement comes which having a change in the test() so create derived class B and override the implementation. And change the pointing object A to B.
Now let's see the composition solution for the same problem.
Create Interface Ai At consumption side change classes to an interface. Implement class B implement the Ai interface with new implementation changes in the test() method And externalized the dependency of the consumption class. Pass the b object Instead of the A object. I preferred the composition solution over the inheritance.
Lesko substitution principle (LSP).
Lisko substation principle says that " Subtypes should be substitutable for the base type" We know that in the inheritance concept base class reference can hold the object of child class object. consider below example
In this example manager is also an employee and employee class reference can hold the object of the manager. then must have a question what is the big deal in that that we do on regular basis. what can be a possible violation in this relationship? We generally use inheritance to reuse the code and we are trying to use it wherever is possible without thinking that there is really any relationship between them or not. The principle is more focused on the real relationship in which child class must be seamless replace the parent class without breaking the application.
So let's see how the violation can happen for this principle. In the above example, there is a violation of the Lesko substitution principle in for loop. Printing employees based on the type means the child object is not fully substituted for the parent class. this is a little heavy to digest but we have to see are violating the LSP or not. Here are the so checks on how to identify the LSP in the code.
- if child class inherited method throws method not implemented exception then the child is not fully substitutable for a parent. This code can compile but break on runtime.
- If there is a type check of happens and based on that an alternative method is called then it's again a violation of LSP.
Solution.
Instead of the external method which does things on the basis of type, we can move that method in the type itself so the type can be seamlessly compatible with a parent.
Interface segregation principle (ISP). "ISP says the client does not force to implement an interface that he does not want".
In other words, the design should not force a client to implement the interface. if it doesn’t really need it. This mostly occurs when the interface contains more than one un-related method. Now we are connecting the dots that every principle is interdependent on each principle. If we don't follow the Single responsibility then it will cause additional responsibility and additional responsibility leads Fatty unrelated interfaces or classes which leads to the violation of ISP.
we should break a single interface into multiple interfaces in either a hierarchical or independent manner. In short, many small and client-specific set of interfaces are more preferable to big or fat interfaces with many methods.
this the solution below
Dependency Inversion Principle.
Dependency inversion principles say "High-level module does not depend on the low-level module and details should depend on the abstraction." this is a little heavy definition to understand.
So Let's understand with a simple understanding what is dependency.
When your class is dependent on another class to perform another task then consider this is the dependency. If you are constructing this dependency in your class then it is the voilation of this principle. This principle says to invert dependency to outside. means just collect it from outside don't construct by our own. Does it sound simple to understand. So then come to the next part of the principle.
this dependency class should not collect with the Details (it means class reference). This dependency should be collected via abstraction it means Interface.
now you must understand what this principle is saying behind those heavy words :) while learning the open-close principle we have taken the example and we fix it so this same example is the voilation of Dependency inversion principle as we are directly creating an object of Connection logger and Smtp client and in the fixed part, we created separate classes for their responsibilities.
now hear we are going one step ahead from that solution In the consumer section, we collected references in the details(Actual concrete class ) The fix of Dependency inversion principle voilation is Hide implementation behind an interface and collect the object in the interface.
So what benefit we by doing following the solid principle.
- We can easily do unit testing
- Every component will have a separate responsibility
- Code is more readable
- We can achieve high cohesion and loose coupling between the component classes.
- Fewer chances of bugs
- If bugs come then identification of changes will be very easy and testing doesn't need regration. only the changed component is sufficient to validate the testing.
- Design is very much flexible for acceptance of new incoming changes which is the most important part of the Software development lifecycle
- You will feel proud on your code basically job satisfaction.
I hope you learn something and appreciate my efforts Thank you for reading Happy learning.