Author: David Martinez
Created: November 23, 2024 - Updated: November 24, 2024
Read Time: 10 min
Welcome! Today, letโs uncover the secrets of the Adapter design pattern. ๐ ๏ธ We'll explore its purpose, applications, and see a real-world example in Ruby that demonstrates its power in action.
The Adapter is a structural design pattern that enables two incompatible interfaces to work together. Think of it as a translator that bridges the gap between systems or classes that otherwise couldnโt collaborate.
This pattern is especially helpful when integrating third-party libraries or legacy systems with your codebase.
๐ธ Use the Adapter pattern when you want to reuse an existing class but its interface is incompatible with the rest of your code.
๐ธ It's particularly useful when dealing with third-party APIs or legacy systems that cannot be changed.
Imagine youโre building a payment system that works with PayPal. Now, a client requests integration with Stripe. Unfortunately, the APIs for PayPal and Stripe differ in how they process payments. Modifying the existing code for every new API would lead to high maintenance costs and potential bugs.
The Adapter pattern provides a bridge between your application and the Stripe API, allowing it to seamlessly integrate without altering the existing PayPal implementation.
By creating an adapter for Stripe, you make its interface compatible with the interface your application already uses.
In this example, the Adapter pattern is applied to unify the interaction between PayPal and Stripe.
๐น The Target Interface (PaymentGateway) defines the standard interface expected by the client code.
๐น The Adaptee (StripeGateway) has an incompatible interface.
๐น The Adapter (StripeAdapter) translates requests from the client into a format the Adaptee can understand.
1๏ธโฃ Define a target interface that the client code uses (PaymentGateway).
2๏ธโฃ Implement the target interface in the adapter class (StripeAdapter) and delegate calls to the Adaptee (StripeGateway).
3๏ธโฃ The client code interacts with the adapter as if it were the target interface, without worrying about the underlying differences.
๐ก Note: This pattern is excellent for making legacy or third-party code work with your application.
๐ฎ Facilitates integration with third-party libraries or APIs.
๐ฎ Minimizes the need for changes in existing code.
๐ฎ Promotes code reusability by allowing systems with incompatible interfaces to collaborate.
๐ฎ Provides a scalable approach for handling future integrations.
# Payment Gateway interfaceclass PaymentGatewaydef pay(amount)raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'"endend# Client-facing classclass PaymentProcessordef initialize(payment_gateway)@payment_gateway = payment_gatewayenddef process_payment(amount)@payment_gateway.pay(amount)endend# Existing PayPal integrationclass PayPalGateway < PaymentGatewaydef pay(amount)"Paying #{amount} with PayPal"endend# Stripe's API with a different interfaceclass StripeGatewaydef charge(amount_in_cents)"Charging #{amount_in_cents / 100.0} with Stripe"endend# Adapter for Stripe to work with our systemclass StripeAdapter < PaymentGatewaydef initialize(stripe_gateway)@stripe_gateway = stripe_gatewayenddef pay(amount)amount_in_cents = (amount * 100).to_i@stripe_gateway.charge(amount_in_cents)endend# Example Usagedef main# Using PayPalpaypal_gateway = PayPalGateway.newpayment_processor = PaymentProcessor.new(paypal_gateway)puts payment_processor.process_payment(100)# Output: "Paying 100 with PayPal"# Using Stripe through the adapterstripe_gateway = StripeGateway.newstripe_adapter = StripeAdapter.new(stripe_gateway)payment_processor = PaymentProcessor.new(stripe_adapter)puts payment_processor.process_payment(100)# Output: "Charging 100.0 with Stripe"endmain
The Adapter pattern provides a powerful way to integrate systems with incompatible interfaces.
๐บ By leveraging this pattern, you can connect legacy systems or third-party APIs with your application effortlessly.
๐บ It reduces the overhead of rewriting or heavily modifying existing code.
๐บ The Adapter pattern promotes reusability and simplifies future integrations.
๐บ It adheres to the Open/Closed Principle by enabling extensions (e.g., new APIs) without altering existing code.
๐ป You can find this and other design patterns here ๐