Cookies Psst! Do you accept cookies?

We use cookies to enhance and personalise your experience.
Please accept our cookies. Checkout our Cookie Policy for more information.

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

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

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.

Last Stories

What's your thoughts?

Please Register or Login to your account to be able to submit your comment.