Solid principles
Solid principles circus analogy

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:

  • Storing data
  • Validating informations
  • Managing performance

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:

  • Clarity and Maintainability: Clear separation of concerns makes code easier to understand and modify.
  • Reusability: Well-defined responsibilities allow classes to be reused in different contexts.


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:

  • Easier Extension: We can introduce new functionalities without modifying existing code. This allows the code to adapt to changing requirements without major refactoring.
  • Enhanced Flexibility: We can easily swap out implementations or add new behaviors without altering the overall structure. This allows for better customization and adaptation to specific needs.
  • Improved Maintainability: Changes become more isolated. When modifications are needed, they're likely confined to the new classes implementing the extended behavior, minimizing the risk of bug regressions.


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:

  • Reliable Subtyping: LSP enables writing code that operates on the base class and can also benefit from the specialized functionality of subclasses without worrying about unexpected behavior.
  • Improved Code Readability and Maintainability: When subclasses correctly extend the base class, the code becomes more readable and maintainable.


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:

  • Reduced Coupling: Clients depend only on the interfaces they use, leading to a more modular and decoupled design.
  • Increased Maintainability and Reusability: Smaller, more focused interfaces are easier to understand, maintain, and reuse across different parts of the codebase.


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:

  • Increased Loose Coupling: DIP promotes loose coupling by relying on abstractions instead of concrete implementation.
  • Improved Testability: DIP allows to easily mock or inject dependencies during testing, isolating the class under test and making tests more reliable and independent of specific implementations.
  • Enhanced Reusability: Code that adheres to DIP is more reusable. Classes don't become locked into specific implementations, allowing them to work with different implementations of the same abstraction depending on the context. This reduces the need for code duplication.


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!

Soumya Ranjan Sahu

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!

Emna Ayadi

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 !

Rahul Abrol

Android | Jetpack Compose | kotlin | Java | MVVM | Clean Architecture | Design Patterns

7 个月

Amazing article ???? thanks for sharing ????

要查看或添加评论,请登录

社区洞察

其他会员也浏览了