The invention of SOLID principles began in the late 80s. Robert C. Martin started to develop these principles while arguing the principle of software design on USENET (an early kind of Facebook). After addition and subtraction, Robert C. Martin formulated the principles in the early 2000s. It was until 2004 that the principles were arranged and called SOLID principles. It is an acronym that stands for five specific design principles.
This principle was described in the work of Tom DeMarco¹ and Meilir Page-Jones². They called it cohesion. They defined cohesion as the functional relatedness of the elements of a module.
In fact, this principle might be the least well understood because has a particularly inappropriate name. Many developers understood that every module should do just one thing. Make no mistake, there is a principle like that. But it is not one of the SOLID principles and it is actually not the SRP.
It has been described as follows: “Each software module has one, and only one, reason to change”. Because, software systems are changed to meet user demands, to satisfy stakeholders, so we can rephrase the principle to say this: “Each module should be responsible to one, and only one, user or stakeholder”. But there will likely be more than one user or stakeholder who wants the system to change in the same way, we called them as an actor or a group, so the final version is: “Each module should be responsible to one, and only one, actor”.
Let assume that we have an Employee object, it has three functions: calculatePay(), reportHours() and save().
Unfortunately, it is violating the SRP because those three functions are responsible for three different actors.
So, the way to avoid this problem is to separate code that supports different actors.
The EmployData object to save a shared simple data structure, it used by all three actors.
The PayCalculator object has the
The HourReporter object has the
The EmployeeServer object has the
It is how we use the SRS to refactor bad codes. Each function is responsible for a specific actor. The SRP is one of the simplest of the principles, and one of the hardest to get right.
Bertrand Meyer made this principle famous in the 1980s, which appeared in his book Object-Oriented Software Construction³. Software systems be designed to allow the behavior of those systems to be changed by adding new code, rather than changing existing code.
The OCP states the following: “Software entities (classes, modules, functions, and so on) should be open for extension, but closed for modification” by Meyer, Bertrand. This principle advises us to refactor the system so that further changes of that kind will not cause more modification. Most developers recognize the OCP as a principle that guides them in the design of classes and modules.
There are two primary attributes in the OCP, they are.
It would seem that these two attributes are at odds with each other because the normal way to extend the behavior of a module is to make changes to the source code of that module.
Let assume that every employee has a role and granted privileges. But how if we introduce a new role to the system and don’t modify existing things. So rather we can do like the below example to make it pass the OCP.
So as the above example, we don’t have to modify the existing code rather we can extend it to add a new role. The OCP is one of the driving forces behind the architecture of systems. The goal is to make the system easy to extend without incurring a high impact of change.
Barbara Liskov’s famous definition of subtypes, from 1988 in a conference keynote address titled Data Abstraction and Hierarchy. In short, this principle says that to build software systems from interchangeable parts, those parts must adhere to a contract that allows those parts to be substituted one for another.
One of the classic examples of this principle is a rectangle having four sides. A rectangle’s height can be any value and width can be any value. A square is a rectangle with equal width and height. So we can say that we can extend the properties of the rectangle class into square class. In order to do that you need to swap the child (square) class with parent (rectangle) class to fit the definition of a square having four equal sides but a derived class does not affect the behavior of the parent class so if you will do that it will violate the Liskov Substitution Principle.
Consider that we have an application that uses a rectangle object defined as follows.
Based on the knowledge that a square is a rectangle whose sides are equal in length, we decide to create a square object to use in place of a rectangle.
Unfortunately, a problem is discovered when the application attempts to use our square in place of a rectangle. It turns out that one of the methods computes the rectangle’s area like so.
When the method is invoked with a square, the product is 16 rather than the expected value of 12. Our square object violates the Liskov Substitution Principle with respect to the area function. In this case, the presence of the length and width properties was a hint that our square might not end up being 100% compatible with the rectangle, but we won’t always have such obvious hints.
This principle advises software designers to avoid depending on things that they don’t use.
Suppose if you enter a restaurant and you are pure vegetarian. The waiter in that restaurant gave you the menu card which includes vegetarian items, non-vegetarian items, drinks, and sweets. In this case, as a customer, you should have a menu card which includes only vegetarian items, not everything which you don’t eat in your food.
Here the menu should be different for different types of customers. The common or general menu card for everyone can be divided into multiple cards instead of just one. Using this principle helps in reducing the side effects and frequency of required changes.
Let’s say some settings we have to do in the constructor. The settings we do should be segregated from the other unwanted settings in the constructor.
Here, validateUser() function will get invoked in initiateUser()constructor call even though it’s not needed all the time. We can bring this into ISP with the following code.
As the above code, we are segregating the unwanted logic from the contractor function.
Before we discuss this topic keep in mind that Dependency Inversion and Dependency Injection both are different concepts. Most of the people get confused about it and consider both are the same. Now key points are here to keep in mind about this principle.
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. Rather, details should depend on policies.
You can consider the real-life example of a TV remote battery. Your remote needs a battery but it’s not dependent on the battery brand. You can use any XYZ brand that you want and it will work. So we can say that the TV remote is loosely coupled with the brand name. Dependency Inversion makes your code more reusable.
Let take a simple example to understand how we can do it easily.
I want to contact the server for some data. Without applying DIP, this might look like as follows.
With DIP, I might instead write code like.
Where the abstraction
function can for the particular case where we want to use jQuery's Ajax be implemented as follows.
The abstraction view can be implemented for the particular case of a view based on elements with IDs thingy1 and thingy2 as follows.