Solid principles
In this article we're going to explore the SOLID principles, a set of guidelines that help developers build more flexible, understandable, and maintainable code. We'll use a fun analogy: a circus! ??
So grab your popcorn, and get ready to learn some cool tricks!
In "SOLID" each letter stands for a principle:
Single Responsibility Principle (SRP)
Picture a clown whose only mission is to make you laugh. Just like our clown has one clear purpose, a class should have only one reason to change, ensuring it's easy to understand, maintain, and reuse.
Let's start by an exemple that violates the SRP:
The clown class combines multiple responsibilities:
This makes the method complex and hard to understand. If you need to modify one aspect (e.g., validation logic), you might unintentionally break another (e.g., data storage).
To adhere to SRP, we can refactor the class into separate classes, each with a single responsibility:
Benefits:
Open/Closed Principle (OCP)
Imagine a magician pulling various objects from a hat without changing the hat itself. Similarly a class should be open for extension but closed for modification, allowing you to add new features without changing existing code.
First let's see how the example below violates OCP:
The pullOutFromHat method in the Magician class uses a when expression to determine which object to pull out based on itemType parameter. If a new object (e.g., "Dove" or "Card") needs to be added, the pullOutFromHat method must be modified to include an additional condition.
Let's see how we can refactor this to adhere to OCP:
Now, the Magician class can perform any act that implements the HatAct interface without needing to change. We can add more acts (e.g., DoveAct, CardAct) by creating new classes that implement HatAct without modifying the existing classes or the Magician class.
Benefits:
Liskov Substitution Principle (LSP)
Think of a child elephant that can perform the same tricks as an adult elephant and can replace its parent in the show without issues, subclasses can replace their base classes without breaking the app, making the code more reliable, readable and maintainable.
Let's see how the example below violates LSP:
According to LSP, objects of a superclass should be "substituted" (replaceable) with objects of its subclasses without affecting the correctness of the program. In this case, replacing an Elephant object with a ChildElephant object in the showElephantTrick function leads to a runtime error (exception), breaking the application.
领英推荐
To adhere to LSP, this exception should be removed:
Benefits:
Interface Segregation Principle (ISP)
Imagine a cotton candy stand where the menu is organized by colors (white, pink, blue). This allows customers to easily locate the price of their desired color without having to scan the entire menu, similar to how dividing large interfaces into smaller, more focused ones prevents a class from implementing unnecessary methods.
Let's look at an example that violates ISP:
The Client class has empty implementations for onPinkSelect and onBlueSelect, indicating that they are not needed for this particular client. This is a clear sign that the interface is not properly "segregated", as the client is forced to implement functions that it does not use.
In order to fix this let's divide this large interface into smaller ones:
A class can choose to implement only the interfaces that are relevant to its needs, in this case, the Client class implements only the WhiteCottonCandy interface and the AnotherClient class implements both the PinkCottonCandy and BlueCottonCandy interfaces.
Benefits:
Dependency Inversion Principle (DIP)
When you get a circus ticket, you don't need to know how the circus is organized or managed. You just want to enjoy the show. Similarly, high-level modules shouldn't depend on low-level modules. Components should rely on abstractions (interfaces) instead of concrete implementations, promoting flexibility and decoupling.
Here is an example where DIP is violated:
The Spectator class is tightly coupled to the CircusShow class. If the CircusShow class changes (e.g., a seatNumber property is added), the Spectator class and all other classes that depend on CircusShow would need to be modified.
This also could lead to OCP violation, If we want to allow the spectator to buy tickets for different types of shows (e.g., a concert or a theater performance), we would need to modify the Spectator class to include additional dependencies (e.g., ConcertShow("The big Concert"), TheaterShow("Excellent performance")).
To adhere to DIP, we should depend on an interface in the Spectator class. Here's how we can refactor the example to follow DIP:
As you can see we've added a seatNumber parameter without the need to modify the spectator class.
Benefits:
Conclusion
Adhering to all SOLID principles might be challenging at first, but consistent effort and practice will lead to a clean, flexible, maintainable and reliable code.
I hope this article has clarified the SOLID principles for you, and that the circus analogy has helped you remember them.
Thank you for reading! Share this article if you found it helpful, and happy coding!
Android Tech Lead at Fiserv India ll Android 10+ YOE || Kotlin || Java || DS || React Native || MVVM || MVI || COMPOSE || CICD || HILT || REDUX || FLOW || WORK MANAGER || ROOM || CO-ROUTINE || JENKINS || JETPACK || UT
7 个月Superb article!
Agile Quality Coach at Sogeti | Linkedin Author | #?????????????????????????????? | International Speaker | SogetiLabs Fellow
7 个月Great write-up Marwa Nasrallah ! Glad to see you embarking in the blogging journey and even further for making it fun and more accessible for non developers with the ?? comparaison and good vibes. Keep going ???? I look forward for your future content !
Android | Jetpack Compose | kotlin | Java | MVVM | Clean Architecture | Design Patterns
7 个月Amazing article ???? thanks for sharing ????