Open Closed Princeple – Deep Dive

Intro

“Principles are the basis for developing a vision and value system for all.” ~ Stephen Covey

 

The above quote emphasizes the importance of taking principles seriously. As a software developer, learning and practising principles, patterns along with best practices will eventually equip you with a black belt to be used when thinking of a coding problem. 

 

Now let’s finish the dramatic intro and get to business 🤓.

 

In this post, we will discuss the second principle of SOLID, which is the “Open/Closed Principle (OCP)”. In fact, we will deep dive into it, so get ready. In addition, we will implement the principle by writing a code for a case scenario.

To begin with, this principle was introduced just a couple of decades back by (Bertrand Meyer, 1988) 😵 . He defined the principle as follows.

 

Software entities (classes, packages, functions) should be open for extension, but closed for modification.

meme about open closed principle

By now you might be confused especially that the definition of the principle has two negating words Open and Close. Don’t worry, my job is to simplify the principle for you. Going Further, this post is structured as follows.

  • Real-life example
  • The intent of the principle
  • Online shopping app case scenario 
  • E-payment gateway assumptions
  • Writing the scenario code
  • Final Thoughts.

Real Life Example

Imagine that you own a house and you wanted to have additional space 🤔. The logical options will be adding a new floor if the house base can handle the load, or adding an extension room in the yard if enough space is available.

It would be unsafe and highly expensive to demolish some parts of the house to have more space. For instance, one can demolish a wall between two rooms to have a bigger room. However, doing so may put the house structure in jeopardy, and there will be a slight chance of the ceiling fall down 😱     

The above example demonstrates that once the house is built, it will be very hard and expensive to restructure it internally. On the other hand, It is far easier to add a new floor or extension room. Therefore, this what the principle is all about, the house should not be modified internally once it’s built (closed for modification), instead add more by extending the current built house (open for extension). 

At the software development level, this means that new features should be implemented by extending the current implementation instead of modifying the core part of it.  Furthermore, this principle will be much easier to understand with a code example, and we will soon do so. But before that, let’s understand the intent of this principle.

The intent of the principle

Assuming that you have a class at its finest level, tested, reviewed and have no issues. In fact, other parts of the code use some methods from this finest class as well.

Now another member of the team wanted to modify this class to occupy a new feature 🤯

Wouldn’t that be risky?

if the modification broke the class, then all other classes that call this class will get affected. We should have a way to add the new feature without modifying the finest class.

Therefore, the intents of this principle are:

  • decrease the footprint of changes in a source code. 
  • enhancing code reliability as the code modification will be less. 
  • it creates a plug-and-play environment where new features could simply mean adding an implementation in a subclass. 

Going further, we will now go through a case scenario to enhance our understanding. In this case, your role will be a Backend Developer and responsible to integrate the e-payment gateways of a shopping mobile application

Side note: going to the case scenario, I don’t expect you nor want you to memories any part of the code. I want you to analyse the reasoning behind choosing this principle for this case.   

Case Scenario 

mobile application ui used to demonstrate open closed principle
Designed by Heba Zatar, Customized by Anwar Al Jahwari

Assume that you are in the process of developing an amazing shopping mobile application for your company, and your current task is to decide which payment gateways will the application use.

mobile application ui used to demonstrate open closed principle

After meticulous analysis of payment gateways and blessings from the upper management, the chosen payment gateways turned to be Paypal and Stripe.

Going further, the company launched the application with the two e-payments gateway options. However, after a couple of months, they noticed through an analytic tool that most customers do not proceed with the payment 😨

meme used for open closed principle

Therefore, the company launched a survey on social media platforms to identify the issue. It turns out that customers do not trust such payment solutions and prefer local e-payment solution to proceed with payment, and as the quote says, the customer is always right.

Therefore, you were given another task to add a local e-payment gateway to the application.

Now it’s your time to shine and write the code 🤓. To do that, you will need to know the requirements. Therefore, let’s have an assumption about how e-payment gateways work. 

 

E-payment Gateway Assumptions

For this example, we will assume that all payment gateways work in the same way. Below are the expected input and outputs of the e-payment gateway.

Input

  • Order information (total price, product name, etc),
  • MarchantID (unique id of the company).  

Output

  • Link to complete the payment through a web browser

steps to produce e-payment link used to demonstrate open closed principle
Payment Link Generation Steps

The above picture illustrates the steps of producing the link for payment. 

  1. The customer proceeds with the purchase from the cart and chooses an e-payment solution. Then the mobile application sends the order details and chosen e-payment gateway to the backend. 

  2. The backend sends the payment request to the third party e-payment solution. 

  3. The backend sends back the generated link from the third-party e-payment solution to the mobile application. Then the mobile application opens the link in a web view

Writing the scenario code using Java

Finally 😩, before jumping to the code let’s define a folder structure for it. But wait, I think you will wonder why I choose Java? Well, the answer is nothing special, it was just the first language that popped into my head. However, you should be able to write the code in any object-orinted programming language. Going back to the topic, below is the folder structure for this case scenario. 

java code example to demonstrate open closed principle

1- The entry point of the application 

2- model: this folder will contain classes with the main purposes of holding data (POJO). 

3- domain:  this folder will hold the core implementation of any functionality based on the functionality scope. For instance, the implementation of the e-payment will be under a new folder called payment inside the domain folder.

4- common: this folder will contain helper classes, that can be used from any domain. 

Side Note:  for simplicity, i am not following any best practices when it comes to folder structuring, its a whole another story for another post.

Going to the code part, I have created 5 easy steps to accomplish it. In addition, you can get the code with additional comments at the link below. 

https://github.com/better-dev-io/solid_ocp_java_example

Step 1

Create a class called PurchaseOrder under the model folder as shown below.

package com.ocp_example.model;

public class PurchaseOrder {
    String productName;
    int quantity;
    int price;
    int userId;
    int paymentType;

    public PurchaseOrder(String productName, 
    int quantity, 
    int price, 
    int userId, 
    int paymentType) {
        this.productName = productName;
        this.quantity = quantity;
        this.price = price;
        this.userId = userId;
        this.paymentType = paymentType;
    }

    public int paymentType() {
         return this.paymentType;
    }
}

Step 2

Create a new folder called payment under the domain folder. Then create an interface called PaymentGateway As following.

java code example to demonstrate open closed principle

The interface will hold one method called generatePaymentLink, which takes the PurchaseOrder class as a parameter. 

package com.ocp_example.domain.payment;

import com.ocp_example.model.PurchaseOrder;

public interface PaymentGateway {
   String generatePaymentLink(PurchaseOrder purchaseOrder);
}

Step 3

Now will create two classes PaypalPaymentGateway and StripePaymentGateway that implements the interface PaymentGateway. For now, we will return a string that mimics the generated payment link

package com.ocp_example.domain.payment;

import com.ocp_example.model.PurchaseOrder;

public class PaypalPaymentGateway implements PaymentGateway 
{

    @Override
    public String generatePaymentLink(PurchaseOrder po) 
    {
        // amazing code to get the link from payapl
        
        
        return "https://generated-by-paypal.com/"
        .concat("random-generated-link");
    }
}
package com.ocp_example.domain.payment;

import com.ocp_example.model.PurchaseOrder;

public class StripePaymentGateway implements PaymentGateway {

    @Override
    public String generatePaymentLink(PurchaseOrder po)
    {
        // fantastic code to get the link from stripe
    
        return "https://generated-by-stripe.com/"
        .concat("random-generated-link");
    }
}

Step 4

Create a singleton class that is responsible to provide the e-payment gateway. 

Side note: this class is a good example of  Single Responsibility Principle

Furthermore, the class will hold a Map of e-payment gateways and a method called ePaymentGatewayByID with an integer as a parameter to get the required e-payment gateway.

package com.ocp_example.domain.payment;

import java.util.HashMap;
import java.util.Map;

public class EPaymentGatewayProvider {

    Map<Integer, PaymentGateway> ePaymentGatewaysList = new HashMap<Integer, PaymentGateway>() {{
        put(1, new PaypalPaymentGateway());
        put(2, new StripePaymentGateway());
    }};

    private static EPaymentGatewayProvider paymentProviderInstance = null;

    public static EPaymentGatewayProvider getInstance() {
        if (paymentProviderInstance == null)
            paymentProviderInstance = new EPaymentGatewayProvider();

        return paymentProviderInstance;
    }


    public PaymentGateway ePaymentGatewayByID(int paymentTypeID) throws Exception {

        boolean ePaymentTypeExist = ePaymentGatewaysList.containsKey(paymentTypeID);

        if (ePaymentTypeExist) {
            return ePaymentGatewaysList.get(paymentTypeID);
        }

        throw new Exception("Payment Method Dose Not Exist");
    }
}

Final Step

The next step is to write the code that will mimic a new purchase order in the Main class. As shown above, we will first create a new purchase order.

Then, we will get an instance of EPaymentGatewayProvider class to get the payment provider based on the payment type from the PurchaseOrder object

Finally, we will call the generatePaymentLink method and print out the result 🥳👏 You can proudly run the application and it should result in the output from the Paypal gateway since that we provided 1 as the payment type.

package com.ocp_example;

import com.ocp_example.domain.payment.PaymentGateway;
import com.ocp_example.model.PurchaseOrder;
import com.ocp_example.domain.payment.EPaymentGatewayProvider;

public class Main {

    public static void main(String[] args) {

        PurchaseOrder purchaseOrder = new PurchaseOrder(
                "Orange 1kg",
                1,
                12,
                100,
                1);

        try {
            PaymentGateway paymentGateway = EPaymentGatewayProvider
                    .getInstance()
                    .ePaymentGatewayByID(purchaseOrder.paymentType());

            String generatedLink = paymentGateway.generatePaymentLink(purchaseOrder);

            System.out.println("The Generated Link Is");
            System.out.println(generatedLink);

        } catch (Exception exception) {
            System.out.println(exception.getMessage());
        }
    }
}

Now here is the million-dollar question. How you will implement a new local e-payment gateway to the application as the scenario requires? simply, you will have to do the following.

  • Create a new class that implements the PaymentGateway.
  • Add the new class to the ePaymentGatewaysList
  • Add the new payment gateway in the UI and that’s it. Even better, let the UI get the list of providers dynamically. 

As you can observe adding a new payment gateway did not require modifying the main class or any predefined functionality of the code. Instead, we created a new class that implements the PaymentGateway and added it to the provider Map. Therefore, we successfully implemented the Open/Closed Principle in this example. 

Final Thoughts

This principle offers a lot of flexibility when it comes to extending the code. However, as you noticed in this example, the implementation of the principle requires a great understanding of the case or the problem your facing and as I said in a previous post, never rush the code.

Furthermore,  understanding the requirement helped planning the code in a way that will be easy to extend at any moment.

In addition, the main key of this principle implementation is the use of a common interface that we used as a blueprint for all the e-payment gateways.

Finally, do not take anything I write religiously,  you should always perform your due diligence.

Support Ideas

If you liked the content and thought of supporting the creator, below are some ideas to do so.

  • your engagement in the comment will help giving more value to the content.
  • you can simply unblock the ad blocker if you have one
  • spreading the content in social media will help attract more readers and google ranking
Share on facebook
Share on twitter
Share on linkedin
Share on whatsapp
Share on email
You Can

Buy me a Coffee

  • also consider subscribing to the newsletter list

7 Comments

  1. thank you, it’s a very helpful content , but I have some lite question for me in ePaymentGatewaysList map can I separate it into another class to give my code more single responsibility?

    • Hello Awad,

      Thank you for your great feedback.

      The simple answer is yes you can, but I would recommend using a Repository pattern to isolate the data layer from the logic layer.

      In addition, this example can be enhanced far and beyond the scope of the topic.
      However, I keep it simple to offer a space to think and discuss further enhancements.

  2. […] Open Closed Principle – Deep Dive (Anwar Al Jahwari) […]

  3. Very informative series, thank you.

  4. Using Java is the best decision that you made, Object oriented programming take an advantage when using java techniques.
    Thanks for nice informative post, wish you all the best.

    • Thank you, Harith for the lovely feedback.

      Programming languages will always be a controversial topic and each developer has an opinion on it.

      I wish you all the best as well, and see you soon on another topic

Leave a Reply

Physical Address

304 North Cardinal St.
Dorchester Center, MA 02124