DEV Community

Cover image for Open/Closed Principle
Subham
Subham

Posted on

Open/Closed Principle

Links

REPO
Linkedin
Github

Info : Single-responsibility principle - Wikipedia

Info : Open–closed principle - Wikipedia

**software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification";[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:

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');

Enter fullscreen mode Exit fullscreen mode

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:

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);

Enter fullscreen mode Exit fullscreen mode

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.

Top comments (0)