Open-Closed Principle – Simple Explanation with Example

When building applications, we often need to add new features or change behavior. A common mistake is modifying existing code every time we need something new.

This can easily introduce bugs and break parts of the system that were already working.

The Open-Closed Principle (OCP) helps solve this problem.

Single Responsibility Principle

What is Open-Closed Principle?

The idea is simple:

Software entities should be open for extension, but closed for modification.

That means:

  • You should be able to add new behavior
  • Without changing existing code

Why This Matters

When we modify existing code:

  • We risk breaking existing functionality
  • Testing becomes harder
  • The code becomes less stable over time

Instead, we should extend the code in a safe way.

Bad Example (Modifying Existing Code)

Let’s say we have a class that calculates discounts:


class DiscountService {
    calculate(price, type) {
        if (type === "regular") {
            return price * 0.1;
        } else if (type === "premium") {
            return price * 0.2;
        }
    }
}

Problem here:

  • Every time we add a new discount type, we must modify this class
  • This can break existing logic

Good Example (Open for Extension)

Instead of modifying the class, we can extend behavior:


// Base class
class Discount {
    calculate(price) {
        return 0;
    }
}

// Extended classes
class RegularDiscount extends Discount {
    calculate(price) {
        return price * 0.1;
    }
}

class PremiumDiscount extends Discount {
    calculate(price) {
        return price * 0.2;
    }
}

Now we use them like this:


function getDiscount(discount, price) {
    return discount.calculate(price);
}

Why this is better:

  • We don’t change existing code
  • We add new behavior using new classes
  • The system becomes more stable and flexible

Adding a new discount is easy:


class FestivalDiscount extends Discount {
    calculate(price) {
        return price * 0.3;
    }
}

No need to modify old code.

Going One Step Further (Better Design)

In real-world applications, we often combine OCP with dependency injection.


class CheckoutService {
    constructor(discount) {
        this.discount = discount;
    }

    getFinalPrice(price) {
        return price - this.discount.calculate(price);
    }
}

Now we can pass any discount type:


const discount = new PremiumDiscount();
const checkout = new CheckoutService(discount);

console.log(checkout.getFinalPrice(100));

This makes the system flexible and easy to extend.

Real-Life Analogy

Think of a mobile phone:

  • You don’t modify the phone to add new features
  • You install apps to extend functionality

The phone is closed for modification but open for extension.

Key Idea to Remember

  • Don’t modify existing working code
  • Extend behavior using new classes
  • Keep your system stable

Conclusion

At first, modifying existing code may seem easier. But as your project grows, it increases risk and complexity.

By following the Open-Closed Principle, your code becomes:

  • Easier to extend
  • Safer to maintain
  • More scalable


Interface Segregation Principle – Simple Explanation with Example

When building applications, we often create classes or interfaces that contain many different methods. At first, this may seem convenient ...