Open Closed Principle in Java | SOLID Principles

The Open-Closed Principle (OCP) is a core concept in object-oriented design. In this article, we will explore what the Open-Closed Principle means and how to you can apply it effectively in your codebase.

The open closed principle is one of the five design principles of object-oriented design. These set of five principles are known as SOLID principles.

Open Closed principle

The open closed principle states that the software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification.

The two key terms here are open for extension and closed for modification. Let’s understand the meaning of these two terms first.

Open for extension

Open for extension means that a class’s behavior can be extended without modifying its existing code. We should design our classes in such a way that new features or behaviors can be added as requirements evolve, promoting flexibility and maintainability

How we can extend the functionality

1. By using inheritance, we create a superclass that defines common behavior, and for different implementations, we create child classes that extend and customize the functionality of the superclass.

2. By using interfaces, we define a contract that allows multiple implementations, enabling us to substitute different behaviors without changing the existing code.

Closed for modification

Closed for modification means once our module is developed and tested we should not modify it unless there is a bug or any change in that module. For any new requirement or feature, It should be closed for modification.

The idea behind this principle is to design our classes in such a way so that we will be able to incorporate new features/functionality without changing the existing code.

Open closed principle video tutorial

Why do we need open closed principle (OCP)

Let’s understand this with an example. Imagine we need to design a module to process payments through various methods such as credit card, cash, and gift cards etc.

Initially, we support only two payment modes—cash and credit card. To implement this, we create two separate classes: one to handle cash payments and another for credit card transactions.

CashPayment

CreditCardPayment

We also created one PaymentProcessor class which accepts payment mode. The responsibility of PaymentProcessor is to invoke the relevant class based on the mode of payment.

Does this code follows the open closed principle? The answer is no. Because, Imagine in future we have to support more payment modes, to incorporate that we need to change the payment processor class.

The code in PaymentProcessor class change with every new payment mode we add. This is a clear violation of the open/closed principle.

Let’s understand what’s wrong in this code.

i) It clearly impacts the code stability. In our code, we are modifying the same class when we have to incorporate new payment mode. It means we have to retest again all the payment modes. We are compromising the code stability. Any new payment mode risk the stability of previous stable code.

ii) Every new if else block increases code complexity. For every new payment mode, we are adding if else block which increases code complexity and overtime code becomes unmanageable.

How to make code extensible

We can extend the behaviour either using Inheritance or Polymorphism.

The problem with inheritance is that it introduces tight coupling, if the subclasses depend on the implementation details of their parent class.

To address the problem of inheritance, Robert C. Martin redefined the Open/Closed Principle to the Polymorphic Open/Closed Principle. The objective is to use interfaces instead of superclasses. Using interfaces we can provide different implementations without changing the existing code.

Let’s refactor our code using interface. Let’s define an interface IPay which has only one method to accept payment. All the payment mode will implement this interface.

Credit card payment and cash payment implements IPay interface and defines acceptPayment method.

Similarly, When new payment mode is introduced (ex. gift card, etc.). We can implement them using separate class. New class just need to implement this interface.

Let’s see the PaymentProcessor class. In this processPayment() method it only accepts the reference of class which implements IPay interface and invoke it’s accept payment method.

Using this approach we are not modifying the existing stable code. We are extending the behaviour without impacting the existing functionality.

Tagged , . Bookmark the permalink.

About WebRewrite

I am technology lover who loves to keep updated with latest technology. My interest field is Web Development.