avatarAli Zeynalli

Summary

The article discusses the application of the Open-Closed Principle (OCP) within a Spring Boot application, emphasizing the importance of designing software that is open for extension but closed for modification.

Abstract

This article is the second part of a series that explores the application of SOLID software design principles in Spring Boot applications, with a focus on the Open-Closed Principle (OCP). It provides a theoretical background on OCP, which advocates for software entities that are extensible without requiring changes to existing code. The author emphasizes the use of Object-Oriented Programming features like polymorphism and Inversion of Control (IoC) to adhere to OCP. The article illustrates practical examples by refactoring a cash flow management app, introducing interfaces and abstract classes to isolate business logic and prevent transitive dependencies. It also highlights the benefits of this approach, such as reducing the risk of introducing bugs with new features and achieving information hiding. The author provides access to the refactored code and class diagrams on their GitHub repository, inviting readers to examine the changes made to align with OCP.

Opinions

  • The author believes that software systems should be designed to minimize changes to existing code when adding new features, aiming for zero changes to old code in an ideal scenario.
  • The article suggests that careful architectural planning is crucial to protect important components from changes caused by less important components.
  • The author values polymorphism and IoC as powerful techniques in Object-Oriented Programming for achieving the Open-Closed Principle.
  • The author considers the introduction of a new layer of abstraction between Controllers and Services as a positive step towards making Services resistant to changes in Controllers.
  • The article conveys that adhering to OCP leads to a well-structured software system with components that are free from changelings and have a directed dependency flow.
  • The author promotes the idea of using interfaces and implementation classes to extend functionality without modifying the core business logic, thus following the OCP.

How to apply SOLID Software Design Principles to Spring Boot Application (Part 2)

OCP: Open-Closed Principle

Photo by Adrien Olichon on Unsplash

(skip the first paragraph if you have already read other parts of this blog series)

This article is the second 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 second part is about OCP. Before jumping to concrete example, let’s have some theory.

A Software artifact should be open for extension but closed for modification.

This simple description of the principle was firstly introduced by Bertrand Meyer. Software system that needs modification each time for any additional functionality is just a big mess. On the other hand, this kind of chaotic program is welcoming occasional bugs every time modification is made. Preferably, every new functionality should be maximum of new code and minimum of change in old code, in ideal world: zero changes in old code.

From architectural point of view, before structuring any software system every developer should think carefully which components should be free of any changelings, and organize dependency flow so that important components (plugins, packages, classes…) should not need changes when less important components are changed. So dependency flow of a software should be directed to components that you want to protect the most.

If component A should be protected from changes in component B, then component B should depend on component A, not otherwise.

Luckily, capabilities of Object Oriented Programming are able to handle these kind of problems. Polymorphism, by using super/child classes and Inversion of Control, by using interfaces(in java terms!) are two powerful technics that we can use to achieve Open-Closed Principle.

Polymorphism means “many forms”, and it occurs when we have many classes that are related to each other by inheritance.

Inversion of control (IoC) is inverting the flow of control as compared to traditional control flow.

If we recall our cash flow management app, we can see that dependency flow is in one direction: from view to database. In the center of app, there are Service classes that hold the main business logic. The Controllers are injecting Services but not otherwise. So: changes in Controllers wont affect Services. With this kind of one-way binding we made our Services somewhat resistible to changes in Controllers, which is positive effect in respect to OCP.

Class Diagram refactored with SRP(from Part 1)

But let us take this idea to one step further and add another layer of abstraction between Controllers and Services. By now changes in Services will affect Controllers, but applying IoC, we transform Services into interfaces with abstract methods and let them be implemented by ServiceImpl classes which are true implementer of business logic and accordingly isolating Services’ business logic from Controllers. With this we also achieve information hiding: hiding Services from Controllers and avoid transitive dependencies.

Transitive dependencies are a violation of the general principle that software entities should not depend on thins the don’t directly use.

Also we are moving all business logic related modules under cashflow.core.* package. With all these in mind, let us have a look at refactored class diagram of cashflow management. For the sake of completeness, entities are extended with new columns along with other CRUD operations.

Class Diagram refactored with OCP (Part 1)

Like in the first part, you can find relevant branch feature/ocp-refactored-step-1 and source code commits in master branch depicting above given refactoring process.

The first example of OCP was much of an architectural touch to the app. But let’s take a look at one more example with more low-level context. So, there is a need for a new functionality where total amount of incomes and expenses should be calculated. Of course the core calculation can be done in ServiceImpl classes, but it would be not a clever step, since we would modify our business logic instead of extending it. So the right way to implement this functionality by extending the cash flow management is to create a new CalculatorService which will also in future handle all kind of calculations, and let different Use Case implementations bear the code for calculation (see IncomeCalculatorServiceImpl, ExpenseCalculatorServiceImpl classes). So tomorrow if we need a new Use Case to calculate total amount, we wont modify our business logic, but add one more new implementation class. For switching different implementations of the calculation, @Qualifer and @Component annotations are used in Spring Framework Context.

Class Diagram refactored with OCP (Part 2)

The relevant branch feature/ocp-refactored-step-2 with according code refactoring is ready to examine in my git account. All of class diagrams can be also found as .puml files under resources folder .

The third part is dedicated to LSP, Liskov-Substitution Principle. You can find that blog here.

P.S. You can connect with me on twitter or linkedin.

Solid Principles
Open Closed Principle
Refactoring
Software Design
Spring Boot
Recommended from ReadMedium