Wednesday, February 19, 2025

Open-Closed Principle (OCP)

java,programming,software development,technology,design principle
The SOLID principles, a set of five design principles that enhance object-oriented programming (OOP). We discussed the Single Responsibility Principle (SRP) in this article.

SOLID Design Principle is made up of five different principles:
  • S: Single Responsibility Principle (SRP)
  • OOpen-Closed Principle (OCP)
  • LLiskov Substitution Principle (LSP)
  • IInterface Segregation Principle (ISP)
  • DDependency Inversion Principle (DIP)
SOLID Design Principles in Java
This blog post reviews the Open-Closed Principle (OCP) and shows how it can be applied using Java examples.

Open-Closed Principle

This is the second principle of the SOLID design principle group. It tells us:
Classes or modules should be open for extension but closed for modification.
It states that we should not modify any existing classes or modules. Rather, we should create a new entity by inheriting the properties of the existing classes and modules. So, this principle tells us to use inheritance and overriding to extend existing behaviors.

Example without OCP

Assume an e-commerce platform has two discount types: flat discount and percentage discount. Here's our discount system. As you can see, the calculateDiscount(...) method is used to compute a discount on the purchase price based on the discount type.
public class DiscountSystem {
    private Double flatDiscountAmount;
    private Double discountPercentage;
    private Double baseDiscountPercentage;

    // CALCULATE DISCOUNT
    public Double calculateDiscount(EDiscountType type, Double amount) {
        Double discountedAmount;

        switch (type.name()) {
            case "FLAT":
                discountedAmount = amount - (amount * baseDiscountPercentage / 100) - flatDiscountAmount;
                break;
            case "PERCENTAGE":
                discountedAmount = amount - (amount * baseDiscountPercentage / 100)
                        - (amount * discountPercentage / 100);
                break;
            default:
                discountedAmount = 0D;
                break;
        }
        return discountedAmount;
    }

    public Double getFlatDiscountAmount() {
        return flatDiscountAmount;
    }

    public DiscountSystem setFlatDiscountAmount(Double flatDiscountAmount) {
        this.flatDiscountAmount = flatDiscountAmount;
        return this;
    }

    public Double getDiscountPercentage() {
        return discountPercentage;
    }

    public DiscountSystem setDiscountPercentage(Double discountPercentage) {
        this.discountPercentage = discountPercentage;
        return this;
    }

    public Double getBaseDiscountPercentage() {
        return baseDiscountPercentage;
    }

    public DiscountSystem setBaseDiscountPercentage(Double baseDiscountPercentage) {
        this.baseDiscountPercentage = baseDiscountPercentage;
        return this;
    }
}

public enum EDiscountType {
    FLAT, PERCENTAGE;
}
Assume the e-commerce platform wants to introduce another discount system. So first, we'll create a new discount type. Then, create a new case block to implement the new discount logic. After a few days, they plan to introduce another new discount system. So, once again, we must take the same steps to implement the new discount logic.

Every time, we modify the existing code to add new business logic. As a result, these frequent changes increase the risk of breaking existing functionality. This is what the Open-Closed Principle (OCP) says to avoid.

Example with OCP

To implement OCP, we must first define an abstract class with an abstract method. Here's our Discount abstract class. This abstract class has its own property and an abstract method called calculateDiscount(...). This abstract method will be overridden in the class that extends the Discount class.
public abstract class Discount {
    private Double baseDiscountPercentage;

    public abstract Double calculateDiscount(Double amount);

    public Double getBaseDiscountPercentage() {
        return baseDiscountPercentage;
    }

    public void setBaseDiscountPercentage(Double discountPercentage) {
        this.baseDiscountPercentage = discountPercentage;
    }
}

The code snippet below is for FlatDiscountService, which extends the Discount class. It also overrides the calculateDiscount(...) method to incorporate its own discount business logic.
public class FlatDiscountService extends Discount {
    private Double flatDiscountAmount;

    public Double getFlatDiscountAmount() {
        return flatDiscountAmount;
    }

    public FlatDiscountService setFlatDiscountAmount(Double discountAmount) {
        this.flatDiscountAmount = discountAmount;
        return this;
    }

    @Override
    public Double calculateDiscount(Double amount) {
        return amount - (amount * super.getBaseDiscountPercentage() / 100) - flatDiscountAmount;
    }
}

We can develop another new discount service. This is PercentageDiscountService that uses the Discount class.
public class PercentageDiscountService extends Discount {
    private Double discountPercentage;

    public Double getDiscountPercentage() {
        return discountPercentage;
    }

    public PercentageDiscountService setDiscountPercentage(Double discountPercentage) {
        this.discountPercentage = discountPercentage;
        return this;
    }

    @Override
    public Double calculateDiscount(Double amount) {
        return amount - (amount * super.getBaseDiscountPercentage() / 100) - (amount * discountPercentage / 100);
    }
}
Open-Closed Principle (OCP)
If the e-commerce platform wants to introduce a new discount system, it simply creates a new class by extending the abstract one. At the same time, our current discount system will remain unchanged.

Here is our test class to test the discount services.
public class OcpGoodExampleMain {
    public static void main(String[] args) {
        Double purchasedAmount = 4710.78;

        FlatDiscountService flatDiscountService = new FlatDiscountService()
                .setFlatDiscountAmount(250.98);
        flatDiscountService.setBaseDiscountPercentage(1D);
        System.out.println("Actual purchased amount: " + purchasedAmount);
        System.out.println("After flat discount: " + flatDiscountService.calculateDiscount(purchasedAmount));

        System.out.println("-------------------------------------");

        purchasedAmount = 7980.98;
        PercentageDiscountService percentageDiscountService = new PercentageDiscountService()
                .setDiscountPercentage(3.6);
        percentageDiscountService.setBaseDiscountPercentage(1.7);
        System.out.println("Actual purchased amount: " + purchasedAmount);
        System.out
                .println("After percentage discount: " + percentageDiscountService.calculateDiscount(purchasedAmount));
    }
}

Using the Open-Closed Principle (COP), new discount types can be added without changing the Discount class. When we add new discount strategies, the core system is left untouched.

The Open-Closed Principle (OCP) can be applied in various real-world software development scenarios. In those cases, we need to add functionality without changing the existing code. Here are some real-world examples.
  1. Payment Systems: Credit card payments are initially accepted by an online store, but PayPal, Apple Pay, and cryptocurrency payments must be added later.
  2. Logging System: An application begins by logging into a text file, but it must eventually support logging into databases, cloud services (such as AWS CloudWatch), or external monitoring tools.
  3. Notification Services: A notification system sends email notifications at first, but it must eventually support SMS, push notifications, and WhatsApp messages.
  4. User Role Management: A web application has an Admin role, but additional roles such as Editor, Viewer, and Moderator with varying permissions must be added.
  5. Machine Learning Model Deployment: A system begins with a simple linear regression model but eventually needs to support deep learning models, decision trees, and ensemble methods.

Happy coding!!! 😊
in

Popular posts