The article discusses the application of the Interface Segregation Principle (ISP) within the SOLID design principles framework to a Spring Boot application, emphasizing the importance of avoiding unnecessary dependencies in software components.
Abstract
The fourth part of a blog series on SOLID software design principles, this article focuses on the Interface Segregation Principle (ISP), which advises that clients should not be forced to depend on methods they do not use. The author explains the principle's relevance to statically-typed languages like Java and illustrates its application through a case study of a cash flow management application. By refactoring the application to segregate interfaces according to use cases, the author demonstrates how to prevent unnecessary dependencies and potential maintenance issues. The refactoring process involves splitting a monolithic converter service into smaller, more focused interfaces, thereby adhering to ISP and reducing the risk of errors due to changes in unrelated parts of the code. The article also provides a link to a GitHub repository with the refactored code for a deeper understanding of the principle.
Opinions
The author believes that modern IDEs' "Organize Imports" feature, while helpful, does not fully address the complexity of dependencies as outlined by ISP.
The author suggests that dynamically-typed languages may not benefit as much from ISP due to their runtime nature, implying a distinction in the applicability of the principle based on language type.
The author emphasizes the practical benefits of applying ISP, such as reduced compile errors and less need for changes in unrelated parts of the code, indicating a belief in the principle's utility in real-world software development.
The author acknowledges that the example provided may seem trivial with only one method per use case but stresses the importance of ISP in scenarios with more complex interfaces and multiple implementations.
By inviting readers to connect on social media and engage with the code on GitHub, the author shows a commitment to community learning and collaborative understanding of software design principles.
How to apply SOLID Software Design Principles to Spring Boot Application (Part 4)
(skip the first paragraph if you have already read other parts of this blog series)
This article is the fourth part of the blog series, dedicated to well-known software design principles, that evolved over time and were finally summarised by Robert C. Martin with initials of the corresponding principles. These principles guide us how to build well-designed software systems, giving best practices for arranging classes, functions, building blocks. We will look at each principle in depth and apply it to the Spring Boot Application. The idea is refactoring existing software based on these principles from architectural point of view.
The word S.O.L.I.D. stands for:
SRP: Single Responsibility Principle
OCP: Open-Closed Principle
LSP: Liskov Substitution Principle
ISP: Interface Segregation Principle
DIP: Dependency Inversion Principle
This fourth part is about ISP. Before jumping to concrete example, let’s have some theory.
Interface-Segregation Principle states that no client should be forced to depend on methods it does not use.
Before even starting to explain what are insights of this principle, we should make it clear that, this principle is applicable mostly to statically-typed programming languages like Java, C etc. In dynamically typed languages like Python or Ruby this principle does not make a big sense.
Statically-typed language can be referred to the languages where the type of variables is known at the compile time. Dynamically-typed language whereasexecutes many common programming behaviours at runtime.
Based on this principle, software components (plugins, packages, classes …) should not have dependencies that they don’t use. Luckily, at almost all modern IDEs like eclipse or intellij, there is a “Organize Imports” function that will remove unused imports. But the problem is not so trivial.
So if we take a look at our “beloved” cash flow management application, we can imagine a situation that our Income and Expense Use Cases are dependent on a functionality that holds business logic for both cases. So Income Use Case has an abundant dependency on a functionality that is used for Expense Use Case and Expense Use Case has the same issue in respect to Income Use Case. Following is the Violation of ISP based on above discussion:
Since IncomeConverterServiceImpl and ExpenseConverterServiceImpl both must implement unused methods, we are throwing Exception to catch those cases, but it would make sense if we isolate them. So any change in convertExpense() method will lead to compile errors and need for change in IncomeConverterServiceImpl, which even does not need that method. Following is the refactored Class Diagram of Interfaces based on Interface-Segregation Principle in cashflow.core.* Package:
Interface Class Diagram refactored with ISP
So basically, what we did here is to split ConverterServices into Use Cases and let them extend abstract Service Interface. For our example it might be not the best solution since we have only one method to implement for each Use Case. But imagine a situation where you have got lots of interface that are implemented multiple times for different contexts. It is gonna be a big mess, isn’t it? Actually it is all about ISP: split modules into smaller modules to avoid unused dependencies.