Design Patterns: Adapter Pattern
Tags: programming, architecture, beginners, codepen
Published: 2022-11-09 17:05:09

Okay. Let's explore something together. I've recently come across a few open source projects that can really use this pattern / strategy.

So hear me out. Code design patterns exist and are thoroughly documented and taught for a reason. They improve out efficiencies and code reliability. So, why don't more developers use them? I think the answer is because they're normally grouped with Data Structures and Algorithms in schools. Therefore they're thought of as scary. Or, self taught developers (like me) just don't know they exist until someone mentions them. 

I took about 3 years of being a developer to really start learning these concepts. Before then I was building custom software for an ISP. My code was clean. But it was not efficient. So, let's dive into one of my favorite patterns. The `Adapter Pattern`. duh. dong. daaaaaaa.

> Adapter is a structural design pattern that allows objects with incompatible interfaces to collaborate.
https://refactoring.guru/design-patterns/adapter

Okay. So, it some how allows for two objects with completely different methods and attributes to collaborate? That's wild! It's like combining a pizza and a slice of bread into a glorious cake! 

It's easy to understand at a conceptual level. However, when we look at the actual implementation it can get a little tricky. In fact, I think most developers instinctively understand the **adapter** pattern. They usually just lose their way during implementation. 

## Deep Dive Begins

Let's take a deep dive into how this works with a working code example that we will build together. I will use Typescript for strict typing. It is probably most familiar to most people reading this article.

### Defining the problem
We want to implement payment code for our fancy new SAAS app. However, our target demographic is other companies. Those companies may want to use any number of preferred payment providers. 

### Determining potential solutions
We have a few options. We can build out custom payment endpoints on an API for each payment method. The drawback here is having to maintain multiple sections of code that does the exact same thing.

We can build out an **adapter** pattern that contains the same payment code / interface across all payment types. We write the shared interface code once and handle our payment provider specific code in each payment providers code. This abstracts it so that we can ignore complexities.

We can also just tell people to use one payment provider. However, they might not like that. So let's avoid it.

## Writing a sotution

The code below lays the groundwork for our fancy new **adapter** pattern. The code creates a new interface (a set of methods and attributes that code implementing it must have to be valid) that defines methods for our adapters. These methods will be the same no matter what payment provider we are using.

```typescript
interface PaymentsProvider {
  name: string;
  provider: 'stripe' | 'paypal' | 'braintree' | 'adyen' | 'payone';
  
  getPayments(): Promise<Payment[]>
  getPayment(id: string): Promise<Payment>
  createPayment(payment: Payment): Promise<Payment>
  updatePayment(payment: Payment): Promise<Payment>
  deletePayment(id: string): Promise<void>
}

// Define our payment structure
type Payment = {
  id: string;
  amount: number;
  currency: string;
  status: 'pending' | 'completed';
};

// Defines the provider type as a union of defined providers
type Provider = StripeProvider | PayPalProvider;

// Provides and example payment object
let paymentResponse: Payment = {
  id: '123',
  amount: 100,
  currency: 'USD',
  status: 'pending'
}
```

Okay. So we now have a way to define how we should interaction with payment providers. We also built out the payment structure. In addition to the payment structure we built out a sample payment object. We will use all of this later. For now, let's build an example adapter.

```typescript
class StripeProvider implements PaymentsProvider {
  name = 'Stripe';
  provider = 'stripe';
  
  async getPayments(): Promise<Payment[]> {
    return [paymentResponse];
  }
  
  async getPayment(id: string): Promise<Payment> {
    return paymentResponse;
  }
  
  async createPayment(payment: Payment): Promise<Payment> {
    return paymentResponse;
  }
  
  async updatePayment(payment: Payment): Promise<Payment> {
    return paymentResponse;
  }
  
  async deletePayment(id: string): Promise<{ success: boolean }> {
    return { success: true };
  }
}
```

This code is all boilerplate. However, in a real world situation you would write all of your payment provider specific code in these payment provider classes.

```typescript
function Payments () {
  let providers: { [key: string]: Provider };

  return {
    addProvider(provider: Provider) {
      providers[provider.provider] = provider;
    },
    getProvider(provider: string): Provider {
      return providers[provider];
    },
    getProviders(): Provider[] {
      return Object.values(providers);
    },
    removeProvider(provider: string) {
      delete providers[provider];
    }
  }
}
```

This is the provider manager. It'svery similar to a builder method. It's how I prefer to handle these. It's simple and easy to understand while keeping it easy to maintain. Let's move onto seeing it put together!

 ```typescript
const payments = Payments();

payments.addProvider(new StripeProvider());
payments.addProvider(new PayPalProvider());

console.log(payments.getProviders());

payments.getProvider('stripe').getPayments().then(console.log);
payments.getProvider('paypal').getPayments().then(console.log);
```

You can view the full example source code below.

{% codepen https://codepen.io/brentvdalling/pen/PoaWBXo %}

If you found this helpful please leave a like and bookmark. Please leave a comment! Let's discuss where we can use this! If you spotted a problem with the information provided please leave a comment and let me know. Let's have a healthy discussion! Thanks for reading!