S.O.L.I.D. Principles
Hello World!!!
Many times, when we are programming, we feel that we can make it better, but we don’t know exactly what to do or if we are in the right way. In the post about Clean Code it was said that it is necessary a “code-sense”. But how to get this? Where can I start?
Don’t worry. Some people have been thinking about it and they will lead us.
Some time ago, Robert C. Martin, wrote an article about the principles of object-oriented programming that are very useful for every programmer and it will help us to think more clearly about our codes. He spoke about five principles of classes design and six principles about packages.
This post will focus on the first five ones because if you have this knowledge in your mind, your code will be easy to maintain and to extend and also they make it easy for developers to avoid code smells and easily refactor code.
- S - Single Responsibility Principle (SRP)
- O - Open-closed principle (OCP)
- L - Liskov Substitution principle (LSP)
- I - Interface Segregation principle (ISP)
- D - Dependency inversion principle (DIP)
1. Single Responsibility Principle
A class should have one, and only one, reason to change.
It is one of the basic principles most developers apply to build robust and maintainable software. This principle makes your software easier to implement, explain, understand than the ones that provide a solution for everything.
Uncle Bob, in his chapter about SRP, define responsibility: “a reason for a change”. It’s normal if the requirements change and the class has more responsibilities. When this happens you need to change your class. If the class has only one responsibility, the changes are not often and the number of bugs will be reduced.
The picture below shows how Uncle Bob represented in his article a set of classes with more than one responsibility (left) and how he changes the class Rectangle (right).
2. Open-closed Principle
You should be able to extend a classes behavior, without modifying it.
You can add new behaviour, but you shouldn’t break what works and change what exists. If one of them happens so you need to refactor your code.
Uncle Bob notes that some people had confused this principle. Then, he expresses himself in other post saying: It should be easy to change the behavior of a module without changing the source code of that module. This doesn’t mean you will never change the source code. Ideally, you will be able to add the new behavior by adding new code and changing little or no old code.
The next picture shows the example of the article where the class Circle was added and it was necessary add a new method to AreaCalculator. This implementation makes necessary add new methods to calculate each new shape.
The mechanisms behind the OCP are abstraction and polymorphism. Every new shape extends Shape interface and implements a specific behaviour and AreaCalculator doesn’t need to know the type of the object.
The detail about this principle can be seen in the article about OCP with examples. The examples in Java can be seen Java Brahman.
3. Liskov Substitution Principle
Derived classes must be substitutable for their base classes.
Barbara Liskov introduced this principle in a 1987 and Uncle Bob add this in one of the five class design principles, LSP.
This principle speaks about inheritance hierarchies. A subclass can override the parent class’s method but cannot break the behaviour. This requires all subclasses to behave in the same way as the parent class. So, the behaviour of the class becomes more important than its structure.
The next picture, with detailed explanation in another post, shows an implementation of the classic example used by Uncle Bob in his article about LSP.
You can see the relationship Square IS_A Rectangle. But in fact, it is not completely true. A square is a rectangle with equal width and height. If you run a test with Client using a Square object, it will break the test because the properties width and height have the same value.
A solution is to separate the classes and create a new hierarchy where both classes (Square and Rectangle) implement an interface (Geometry) where it is declared a common behaviour.
4. Interface Segregation Principle
Make fine grained interfaces that are client specific.
ISP: “Clients shouldn’t depend on methods they don’t use. Several client-specific interfaces are better than one generalized interface.” It means that it's necessary to take care of the Interface Pollution. The Interface Pollution happens when an InterfaceA has to add an InterfaceB that InterfaceA doesn't require, for the benefit of one of its subclasses. It makes the class "fat", and "fat" classes are hard to maintain and probably are not cohesive. Certainly, it should be split into different groups. The picture below shows an example of the problem (left) where one interface is used for many clients and a solution (right) where the interface is split to be used to specific clients. The detail about this example can be seen here.
5. Dependency Inversion Principle
Depend on abstractions, not on concretions.DIP: “High-level modules shouldn’t depend on low-level modules. Both modules should depend on abstractions. In addition, abstractions shouldn’t depend on details. Details depend on abstractions.” The "inversion" term is used because usually software structures are created in which high-level modules depend on low-level modules and abstractions depend on details. The next picture shows an example of two dependencies. Two concrete classes, PDFBook and EBookReader, depend on EBook interface.
Summary
References
- Principles Of OOD
- Agile Software Development, Principles, Patterns and Practices
- SOLID Design Principles Explained - SRP
- The Open-Closed Principle, In Review
- Java Brahman - OCP
- Java Brahman - SRP
- LSP and ISP
- SOLID: Part 3 - Liskov Substitution & Interface Segregation Principles
- SOLID: Part 4 - The Dependency Inversion Principle