s.o.l.i.d.
SOLID Estimated reading time: 12 minutes- the history
- SRP - Single Responsibility Principle
- OCP - Open-Closed Principle
- LSP - Liskov Substitution Principle
- ISP - Interface Segregation Principle
- DIP - Dependency Inversion Principle
- what’s wrong with SOLID?
- Conclusion
- Resources
Often we are talking about some principles that can improve our code. These principles are also known as SOLID. During the past few discussions, I heard a lot of different explanations (correct and not) for the same things.
In this article, I just would like to cover each principle in detail and provide a short and easy-to-understand explanation.
This is an important point. Principles will not turn a bad programmer into a good programmer. Principles have to be applied with judgment. If they are applied by rote it is just as bad as if they are not applied at all. (Uncle Bob)
the history
At the end of 1980 Robert C. Martin started his discussion in USENET related to best principles of programming. During the decade these principles have a lot of changes and finally at the beginning of 2000 SOLID was created. Name SOLID was proposed by Michael Feathers in 2004. This is how SOLID was created.
- SRP - Single Responsibility Principle
- OCP - Open-Closed Principle
- LSP - Liskov Substitution Principle
- ISP - Interface Segregation Principle
- DIP - Dependency Inversion Principle
It’s good to understand, what is a principle. A good explanation available on CleanCode and written by Uncle Bob:
The SOLID principles are not rules. They are not laws. They are not perfect truths. They are statements on the order of “An apple a day keeps the doctor away.” This is a good principle, it is good advice, but it’s not pure truth, nor is it a rule.
SRP - Single Responsibility Principle
Many developers think, that according to the name, this principle means that every module should be responsible only for something single. And indeed, such a principle correct and exists, but it’s hasn’t any relation to SOLID principles and so does not describe the SRP principle.
The real means of the SRP -
The module should be responsible only for one and just one actor.
where module - is a set of connected functions and data structures. And actor - is a group (from one or a few) of people who wants change. This means, that each software module should have one and only one reason to change
Another wording for the Single Responsibility Principle is:
Gather together the things that change for the same reasons. Separate those things that change for different reasons.
example
One of the main indicators of SRP violation is when a few actors require a change of the same module. If u can see a risk that different developers can start code editing related to the same entity at the same time or/and can make a code duplication then it’s probably an SRP violation.
Here is an example.
Here we can see, that 2 functions require different actors - one is probably a maintenance team and another - a driver.
To solve this, Uncle Bob proposes a lot of variants, and all of them are related to the process of function separation in different classes.
One of the possible solutions can be done using the Facade pattern:
Facade is a structural design pattern that provides a simplified (but limited) interface to a complex system of classes, library, or framework.
OCP - Open-Closed Principle
This principle is known as the most important one. It originated from the work of Bertrand Meyer and created in 1988:
We should write our modules so that they can be extended, without requiring them to be modified. In other words, we want to be able to change what the modules do, without changing the source code of the modules.
In other words, u’r code should be:
- Open for Extension: U must keep u’r code in a state, that requires minimum efforts for any change. I do believe, that if u think about u’r code (some module) and u feel that u don’t want to modify it - this is it - u’r code is dirty and required additional attention and effort.
- Closed for modification: Extending the class should not change the existing implementation.
This can be achieved by using some techniques such as Dynamic and Static polymorphism, for example:
- Inheritance
- Abstraction and composition
- Generics
This principle makes our code more maintainable and extendable. Also, u’r code becomes more stable when a new change is introduced because it’s divided into separate let’s say pats, that protects lower levels of functionality in a hierarchy.
I guess this is why this principle is known as “the most important one”.
example
Here is a quick example of it:
Now, we should somehow add new functionality but do not change the codebase. To do this we can use inheritance or extract interface and conform to it by specifically created objects:
There are a lot more options (using generics, overloading, etc)
LSP - Liskov Substitution Principle
This principle was described firstly by Barbar Liskov in her work regarding data abstraction :
If for each object obj1 of type S, there is an object obj2 of type T, such that for all programs P defined in terms of T, the behavior of P is unchanged when obj1 is substituted for obj2 then S is a subtype of T.
In other words:
Subclasses should be substitutable for their base classes
A subtype doesn’t automatically become a valid substitutable for its supertype. We must be sure, that this type can behave in the same manner as its supertype.
This principle is not just about the bad side of inheritance - too many/deep inherited types can be messy, that the reason why we should use composition over inheritance. The main idea of this principle - “is about keeping abstractions crisp and well-defined.”
example
Consider this example:
Now, imagine that we add one more type of patient:
then:
This is an example of LSP violations. To solve this, we can do next:
ISP - Interface Segregation Principle
From the official paper: “If you have a class that has several clients, rather than loading the class with all the methods that the clients need, create specific interfaces for each client and multiply inherit them into the class”:
Many client-specific interfaces are better than one general-purpose interface
or Clients should not be forced to depend on methods that they do not use.
In other words: Don’t add stuff to me that is not needed for me.
This principle describes a problem with the fat interface - when too many functions/methods/variables are described in the interface, it’s become unmaintainable and so problematic: too many elements, too many responsibilities, too many everything.
example
Imagine, we have an idea to describe a bird:
Looks like everything is fine, but what happens, if we would like to describe another Bird - Penguin:
To solve this, we can create small protocols instead:
DIP - Dependency Inversion Principle
The implication of this principle is quite simple. Every dependency in the design should target an interface or an abstract class. No dependency should target a concrete class.
Depend upon Abstractions. Do not depend upon concretions
The last in list, but not least principle is the essence of development when u have a deal with reusable components - u should decouple dependencies using abstraction. This will improve code reusability.
“Every dependency in the design should target an interface or an abstract class. No dependency should target a concrete class.”
Following this principle not only improve reusability but also bring light to testability.
example
Imagine, that u have the next example:
Now, in case if u have a complex logic inside FileHandle
- how would u test it? Even if u able to create an instance, the logic inside may be time-consuming, which is not ok within FIRST principles for testing… Also, think about reuse - if something will depend on FileHandle
, we can’t easily extend and reuse it. To solve this - here is a DIP:
what’s wrong with SOLID?
During the past few years, there is more and more discussion about efficiency and actuality of some or all principles from SOLID.
If u also think that these principles are irrelevant and old for our time, there is a fresh and complete answer about relevance of SOLID principles from uncle Bob.
I do agree with SOLID, and I do believe that thanks to these principles, my code can become better.
To be honest, I do not’s always follow these principles due to time pressure or some other
imaginaryreasons. But, I do always try to improve my code, and these principles (within another one like DRY, KISS, etc) are very helpful.
Conclusion
Knowing this principle, as was sad by uncle Bob, will not turn u into a good programmer. But, if u understand them, and use them (at least in the most obvious and critical cases), this improves u’r coder’s life.
Using this principle, u can solve few problems related to software development:
- Fragility: When any change in the code may affect another part of the code that u don’t expect.
- Immobility: Big coupling makes the reuse of components impossible.
- Rigidity: Any change requires too many efforts because it affects a lot part of u’r code.
Resources
- What do I mean by “Principle” by Uncle Bob
- Principles and Patterns
- Open-closed principle
- Java OCP example
- The Open-Closed Principle Explained
- Data Abstraction and hierarhy
- Composition over inheritance
- Software Engineering Design & Construction
- SOLID applied swift
- SOLID
- Solid Relevance
- SOLID relevance 2020
Share on: