Open/Closed Principle
Tags: typescript, javascript, softwaredevelopment, systemdesign
Published: 2024-04-28 21:27:41

## **Links**

[**REPO**](https://github.com/Subham-Maity/scalable-system-design)
[**Linkedin**](https://www.linkedin.com/in/subham-xam/)
[**Github**](https://github.com/Subham-Maity)

**Info** : [Single-responsibility principle - Wikipedia](https://en.wikipedia.org/wiki/Single-responsibility_principle)


**Info** : [Open–closed principle - Wikipedia](https://en.wikipedia.org/wiki/Open%E2%80%93closed_principle)

***software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification*";[[1]](https://en.wikipedia.org/wiki/Open%E2%80%93closed_principle#cite_note-:0-1) that is, such an entity can allow its behaviour to be extended without modifying its source code

1. **Open for Extension**:
    - A class or module should be designed to allow new functionality to be added without modifying its existing code.
    - Extensions can include adding new methods, properties, or behaviour.
2. **Closed for Modification**:
    - Once a class or module is stable and tested, it should remain unchanged.
    - Avoid modifying existing code to add new features or alter behaviour.

## Violations of the OCP

Consider the following example of a `PaymentProcessor` class:

```tsx
class PaymentProcessor {
    processPayment(amount: number, paymentType: string) {
        if (paymentType === 'credit') {
            console.log(`Processing credit payment of ${amount} using credit`);
        } else if (paymentType === 'debit') {
            console.log(`Processing debit payment of ${amount} using debit`);
        } else if (paymentType === 'cash') {
            console.log(`Processing cash payment of ${amount} using cash`);
        } else if (paymentType === 'paypal') {
            console.log(`Processing PayPal payment of ${amount} using PayPal`);
        } else if (paymentType === 'stripe') {
            console.log(`Processing Stripe payment of ${amount} using Stripe`);
        } else {
            console.log(`Invalid payment type ${paymentType}`);
        }
    }
}

const process = new PaymentProcessor();

process.processPayment(100, 'credit');
process.processPayment(200, 'debit');
process.processPayment(300, 'cash');

```

In this example:

- The `PaymentProcessor` class handles different payment types (credit, debit, cash, PayPal, and Stripe).
- If a new payment type is introduced, we need to modify the existing class, violating the OCP.

## A Better Approach

To adhere to the OCP, we can use interfaces and separate responsibilities. Here’s an improved version:

```tsx
interface IPaymentProcessor {
    processPayment(amount: number): void;
}

class PaymentProcessor {
    processor: IPaymentProcessor;

    constructor(paymentProcessor: IPaymentProcessor) {
        this.processor = paymentProcessor;
    }

    processPayment(amount: number) {
        this.processor.processPayment(amount);
    }
}

class CreditCardProcessor implements IPaymentProcessor {
    processPayment(amount: number) {
        console.log(`Processing credit card payment of ${amount}`);
    }
}

class PaypalProcessor implements IPaymentProcessor {
    processPayment(amount: number) {
        console.log(`Processing PayPal payment of ${amount}`);
    }
}

const creditCardProcessor = new CreditCardProcessor();
const paypalProcessor = new PaypalProcessor();
const processor = new PaymentProcessor(creditCardProcessor);

processor.processPayment(100);

```

In this improved version:

- We define an `IPaymentProcessor` interface with a `processPayment` method.
- The `PaymentProcessor` class accepts an instance of a payment processor (e.g., `CreditCardProcessor` or `PaypalProcessor`).
- Each payment processor class adheres to the interface and provides its own implementation.
- New payment processors can be added without modifying existing code.

By following the OCP, we achieve better maintainability and flexibility in our software systems. Extensions can be added without disrupting existing functionality, making the system more robust and adaptable to change.