Design Patterns: Adapter Pattern

·

3 min read

The Adapter pattern is a structural design pattern that allows incompatible interfaces to work together. This pattern is suitable for projects where there is a need to integrate different systems or components that have different interfaces or protocols.

One example of a project where the Adapter pattern is useful is in the development of a web application that needs to communicate with a legacy system or an external API. The legacy system or API may have a different interface or protocol than the web application, making it difficult to integrate the two systems. By using the Adapter pattern, a new interface can be created that allows the web application to communicate with the legacy system or API. This can help to improve the integration and interoperability of the systems and make it easier to maintain and modify them in the future.

Another example of a project where the Adapter pattern is useful is in the development of a mobile application that needs to work with different versions of a database or a third-party library. Different versions of the database or library may have different interfaces or methods, making it difficult to maintain and update the mobile application. By using the Adapter pattern, a new interface can be created that allows the mobile application to work with different versions of the database or library. This can help to improve the compatibility and portability of the mobile application and make it easier to add or modify functionality in the future.

Usually, the Adapter pattern is best utilized in projects where there is a need to integrate different systems or components that have different interfaces or protocols.

// Weather API Adapters
class WeatherAPIAdapter {
  constructor(weatherAPI) {
    this.weatherAPI = weatherAPI;
  }

  getTemperature(city) {
    throw new Error('getTemperature method must be implemented');
  }

  getHumidity(city) {
    throw new Error('getHumidity method must be implemented');
  }

  getPressure(city) {
    throw new Error('getPressure method must be implemented');
  }
}

class OpenWeatherMapAdapter extends WeatherAPIAdapter {
  constructor() {
    super('OpenWeatherMap');
  }

  async getTemperature(city) {
    const data = await this.weatherAPI.getData(city);
    return data.main.temp;
  }

  async getHumidity(city) {
    const data = await this.weatherAPI.getData(city);
    return data.main.humidity;
  }

  async getPressure(city) {
    const data = await this.weatherAPI.getData(city);
    return data.main.pressure;
  }
}

class AccuWeatherAdapter extends WeatherAPIAdapter {
  constructor() {
    super('AccuWeather');
  }

  async getTemperature(city) {
    const data = await this.weatherAPI.getData(city);
    return data[0].Temperature.Metric.Value;
  }

  async getHumidity(city) {
    const data = await this.weatherAPI.getData(city);
    return data[0].RelativeHumidity;
  }

  async getPressure(city) {
    const data = await this.weatherAPI.getData(city);
    return data[0].Pressure.Metric.Value;
  }
}

// Weather App
class WeatherApp {
  constructor(weatherAPIAdapter) {
    this.weatherAPIAdapter = weatherAPIAdapter;
  }

  async displayWeather(city) {
    const temperature = await this.weatherAPIAdapter.getTemperature(city);
    const humidity = await this.weatherAPIAdapter.getHumidity(city);
    const pressure = await this.weatherAPIAdapter.getPressure(city);

    console.log(`Temperature: ${temperature}°C`);
    console.log(`Humidity: ${humidity}%`);
    console.log(`Pressure: ${pressure} hPa`);
  }
}

// Usage
const city = 'New York';

let weatherAPIAdapter;

if (useOpenWeatherMapAPI) {
  weatherAPIAdapter = new OpenWeatherMapAdapter(new OpenWeatherMapAPI());
} else {
  weatherAPIAdapter = new AccuWeatherAdapter(new AccuWeatherAPI());
}

const weatherApp = new WeatherApp(weatherAPIAdapter);
weatherApp.displayWeather(city);

In the above code, we define a WeatherAPIAdapter class that provides a unified interface for different weather APIs, and implements

two adapters for the OpenWeatherMap and AccuWeather APIs. Each adapter extends the WeatherAPIAdapter class and implements the getTemperature, getHumidity, and getPressure methods to map the data from the respective API to the unified interface.

We then define a WeatherApp class that takes a WeatherAPIAdapter instance in its constructor, and provides a displayWeather method to display the temperature, humidity, and pressure for a given city using the unified interface provided by the WeatherAPIAdapter.

In the above example, we created a new instance of the appropriate weather API adapter based on the useOpenWeatherMApAPI parameter, and pass it to a new instance of the WeatherApp class to display the weather for the specified city. The WeatherApp class can now handle data from different weather APIs in a consistent way using the unified interface provided by the WeatherAPIAdapter class.

This implementation of the Adapter pattern allows the weather app to integrate with different weather APIs without being tightly coupled to their individual data formats, making it easier to switch between different APIs or add support for new ones in the future.

Did you find this article valuable?

Support Clint by becoming a sponsor. Any amount is appreciated!