Thursday, 12 December 2024

Adapter Method | JavaScript Design Patterns

The Adapter Pattern is a structural design pattern used to enable two incompatible interfaces to work together. In JavaScript, the Adapter Pattern is typically used when you need to integrate a class or library with an existing system but the interfaces do not match. The Adapter pattern helps by creating a "wrapper" around the existing interface so that it can be used seamlessly with the new system or interface.

Key Concepts:

  1. Adaptee: The existing interface or class that needs to be adapted to work with a different interface.

  2. Adapter: The class that implements the Adapter pattern. It wraps the Adaptee and provides the expected interface.

  3. Target: The expected interface or class that the system is designed to work with.

Example of Adapter Pattern in JavaScript

Imagine you have a legacy system that works with a OldPaymentSystem interface, but you want to integrate a new NewPaymentSystem without changing the existing code.

Without Adapter (Incompatible Systems):

// Old system's payment interface

class OldPaymentSystem {

    processOldPayment(amount) {

        console.log(`Processing payment of ${amount} using old system`);

    }

}

 

// New system's payment interface

class NewPaymentSystem {

    processPayment(amount) {

        console.log(`Processing payment of ${amount} using new system`);

    }

}

 

// Legacy system expecting old system's interface

class LegacyPaymentProcessor {

    constructor(paymentSystem) {

        this.paymentSystem = paymentSystem;

    }

 

    process(amount) {

        this.paymentSystem.processOldPayment(amount);

    }

}

 

// Incompatible new payment system with legacy system

const oldPaymentSystem = new OldPaymentSystem();

const legacyProcessor = new LegacyPaymentProcessor(oldPaymentSystem);

legacyProcessor.process(100); // Works fine

 

const newPaymentSystem = new NewPaymentSystem();

// legacyProcessor.process(newPaymentSystem); // This would fail

In the above example, the new system (NewPaymentSystem) can't directly be used by LegacyPaymentProcessor, because it expects the processOldPayment method. This is where the Adapter pattern can help.

With Adapter (Using Adapter Pattern):

// Adaptee (Existing Interface)

class OldPaymentSystem {

    processOldPayment(amount) {

        console.log(`Processing payment of ${amount} using old system`);

    }

}

 

// Target (New Interface)

class NewPaymentSystem {

    processPayment(amount) {

        console.log(`Processing payment of ${amount} using new system`);

    }

}

 

// Adapter (Makes the Adaptee conform to the Target interface)

class PaymentAdapter {

    constructor(oldPaymentSystem) {

        this.oldPaymentSystem = oldPaymentSystem;

    }

 

    processPayment(amount) {

        this.oldPaymentSystem.processOldPayment(amount); // Adapting old interface to new one

    }

}

 

// Now the new system can be used seamlessly with the legacy code

class LegacyPaymentProcessor {

    constructor(paymentSystem) {

        this.paymentSystem = paymentSystem;

    }

 

    process(amount) {

        this.paymentSystem.processPayment(amount);

    }

}

 

// Usage:

const oldPaymentSystem = new OldPaymentSystem();

const adapter = new PaymentAdapter(oldPaymentSystem);

const legacyProcessor = new LegacyPaymentProcessor(adapter); // Using the adapter

legacyProcessor.process(100); // Works with old system through adapter

 

const newPaymentSystem = new NewPaymentSystem();

const newAdapter = new PaymentAdapter(newPaymentSystem);

const newLegacyProcessor = new LegacyPaymentProcessor(newAdapter); // Using the adapter

newLegacyProcessor.process(200); // Works with new system through adapter

Explanation:

  • OldPaymentSystem: The existing system with the processOldPayment method.

  • NewPaymentSystem: The new system with the processPayment method.

  • PaymentAdapter: The adapter that makes the old system compatible with the new interface.

  • LegacyPaymentProcessor: The class expecting the new interface (processPayment) but can work with both the old and new systems due to the adapter.

    In this case, the Adapter allows LegacyPaymentProcessor to work with both old and new systems without needing to modify its original design.

Advantages of Adapter Pattern:

  1. Code Reusability: The Adapter allows reusing existing code (Adaptee) without modifying it.

  2. Flexibility: It lets you integrate systems with different interfaces.

  3. Decoupling: It decouples the code from the specifics of the interfaces.

When to Use the Adapter Pattern:

  • When you want to integrate a new component into an existing system, but their interfaces are incompatible.

  • When working with legacy code that you cannot modify but need to adapt for new functionality.

 

No comments:

Post a Comment