Понимание шаблона адаптера в JavaScript на реальных примерах.

Здравствуйте, разработчики 👋,
Интеграция различных систем или компонентов — распространенная проблема при разработке программного обеспечения.

Часто это связано с работой с несовместимыми интерфейсами, форматами данных или протоколами.

К счастью, шаблоны проектирования предоставляют элегантные решения для решения этих сложностей интеграции.

В этом сообщении блога мы рассмотрим шаблон адаптера, мощный инструмент в JavaScript, который упрощает интеграцию, выступая в качестве моста между несовместимыми интерфейсами.

Мы углубимся в концепцию шаблона адаптера, поймем его структуру и продемонстрируем его реализацию на практических примерах.

👉 Понимание шаблона адаптера:

Шаблон адаптера — это структурный шаблон проектирования, который обеспечивает связь между двумя несовместимыми интерфейсами, выступая в качестве посредника или моста.

Это позволяет объектам с несовместимыми интерфейсами работать вместе без изменения исходного кода. Шаблон адаптера обеспечивает гибкость, модульность и возможность повторного использования кода, что делает его ценным ресурсом для упрощения задач интеграции.

👉 Структура шаблона адаптера:

Шаблон адаптера состоит из трех основных компонентов:

  1. Целевой интерфейс. Представляет собой интерфейс, с которым клиентский код должен работать. Он определяет методы и свойства, которые будет использовать клиентский код.
  2. Адаптируемый: относится к существующему объекту или компоненту, который необходимо интегрировать в клиентский код. Он имеет несовместимый интерфейс, который не может быть напрямую использован клиентом.
  3. Адаптер: адаптер действует как мост между целевым интерфейсом и адаптируемым. Он реализует целевой интерфейс и внутренне обрабатывает связь и перевод запросов между клиентом и адаптируемым.

💻 Пример 1: Адаптация устаревшего кода

Допустим, у вас есть устаревшая кодовая база, в которой используется проприетарная система ведения журналов с нестандартным интерфейсом. Однако вы хотите переключиться на современную библиотеку журналов со стандартным интерфейсом, например на объект console в JavaScript.

Вот как вы можете использовать шаблон адаптера для бесшовной интеграции новой библиотеки ведения журналов:

// Legacy logging system (Adaptee)
class LegacyLogger {
  logMessage(msg) {
    // Non-standard logging implementation
    console.log(`[Legacy] ${msg}`);
  }
}

// Modern logging system (Target Interface)
class ModernLogger {
  log(msg) {
    // Standard logging implementation
    console.log(msg);
  }
}

// Adapter
class LegacyToModernLoggerAdapter {
  constructor() {
    this.logger = new LegacyLogger();
  }

  log(msg) {
    this.logger.logMessage(msg);
  }
}

// Usage
const logger = new ModernLogger(); // New logging system
const legacyLoggerAdapter = new LegacyToModernLoggerAdapter(); // Adapter

logger.log('This is a modern log'); // Logs using the modern logger
legacyLoggerAdapter.log('This is a legacy log'); // Logs using the legacy logger through the adapter

В этом примере LegacyLogger представляет Adaptee, ModernLogger представляет целевой интерфейс, а LegacyToModernLoggerAdapter действует как адаптер.

Адаптер инкапсулирует нестандартный метод logMessage LegacyLogger и предоставляет метод журнала, который соответствует стандартному интерфейсу ModernLogger. С помощью адаптера устаревшая система ведения журналов легко интегрируется с новой библиотекой ведения журналов.

💻 Пример 2. Интеграция платежного шлюза

Предположим, вы создаете приложение для электронной коммерции и вам необходимо интегрировать платежный шлюз для обработки платежей клиентов. Однако разные платежные шлюзы могут иметь свои уникальные интерфейсы и методы обработки транзакций.

Чтобы упростить процесс интеграции и обеспечить согласованный интерфейс для вашего приложения, можно использовать шаблон адаптера:

// Payment Gateway A (Adaptee)
class PaymentGatewayA {
  processPayment(amount) {
    // Payment processing logic for Gateway A
    console.log(`Processing payment of $${amount} with Gateway A.`);
    // Additional implementation specific to Gateway A
  }
}

// Payment Gateway B (Adaptee)
class PaymentGatewayB {
  makePayment(amount) {
    // Payment processing logic for Gateway B
    console.log(`Making payment of $${amount} with Gateway B.`);
    // Additional implementation specific to Gateway B
  }
}

// Payment Processor Interface (Target Interface)
class PaymentProcessor {
  pay(amount) {
    throw new Error('pay() method must be implemented.');
  }
}

// Adapter for Payment Gateway A
class PaymentGatewayAAdapter extends PaymentProcessor {
  constructor(gatewayA) {
    super();
    this.gatewayA = gatewayA;
  }

  pay(amount) {
    this.gatewayA.processPayment(amount);
  }
}

// Adapter for Payment Gateway B
class PaymentGatewayBAdapter extends PaymentProcessor {
  constructor(gatewayB) {
    super();
    this.gatewayB = gatewayB;
  }

  pay(amount) {
    this.gatewayB.makePayment(amount);
  }
}

// Usage
const gatewayA = new PaymentGatewayA();
const gatewayB = new PaymentGatewayB();

const adapterA = new PaymentGatewayAAdapter(gatewayA);
const adapterB = new PaymentGatewayBAdapter(gatewayB);

// Integration with Gateway A
const paymentProcessorA = adapterA;
paymentProcessorA.pay(100); // Processes payment with Gateway A

// Integration with Gateway B
const paymentProcessorB = adapterB;
paymentProcessorB.pay(200); // Makes payment with Gateway B

В этом примере у нас есть два платежных шлюза, PaymentGatewayA и PaymentGatewayB, представляющие Adaptees. Класс PaymentProcessor представляет целевой интерфейс, который определяет метод оплаты, который клиентский код будет использовать для осуществления платежей.

Классы PaymentGatewayAAdapter и PaymentGatewayBAdapter действуют как адаптеры для каждого платежного шлюза соответственно. Они наследуются от класса PaymentProcessor и реализуют метод оплаты, внутренне вызывая определенные методы обработки платежей соответствующих платежных шлюзов.

Используя шаблон адаптера, приложение электронной коммерции может легко интегрироваться с различными платежными шлюзами с помощью интерфейса PaymentProcessor. Адаптеры инкапсулируют сложности уникального интерфейса каждого платежного шлюза и обеспечивают согласованный и унифицированный интерфейс для приложения.

💻 Пример 3: API-интеграция

Представьте, что вы создаете погодное приложение, которое использует данные о погоде из нескольких внешних API, каждый из которых имеет свой уникальный формат данных и структуру API.

Чтобы упростить интеграцию этих API и предоставить унифицированный интерфейс для вашего приложения, можно использовать шаблон адаптера:

// External API 1 (Adaptee)
class WeatherAPI1 {
  getWeatherData() {
    // API-specific implementation and response format
    return {
      temperature: 25,
      humidity: 70,
      windSpeed: 12,
    };
  }
}

// External API 2 (Adaptee)
class WeatherAPI2 {
  fetchData() {
    // API-specific implementation and response format
    return {
      temp: 30,
      humid: 60,
      wind: 8,
    };
  }
}

// Unified Weather API (Target Interface)
class WeatherAPI {
  constructor(adapter) {
    this.adapter = adapter;
  }

  getWeather() {
    const data = this.adapter.fetchData();
    // Common data format for the application
    return {
      temperature: data.temp || data.temperature,
      humidity: data.humid || data.humidity,
      windSpeed: data.wind || data.windSpeed,
    };
  }
}

// Adapters for the external APIs
class API1Adapter {
  constructor(api) {
    this.api = api;
  }

  fetchData() {
    const data = this.api.getWeatherData();
    return {
      temperature: data.temperature,
      humidity: data.humidity,
      windSpeed: data.windSpeed,
    };
  }
}

class API2Adapter {
  constructor(api) {
    this.api = api;
  }

  fetchData() {
    const data = this.api.fetchData();
    return {
      temperature: data.temp,
      humidity: data.humid,
      windSpeed: data.wind,
    };
  }
}

// Usage
const api1 = new WeatherAPI1();
const api2 = new WeatherAPI2();

const api1Adapter = new API1Adapter(api1);
const api2Adapter = new API2Adapter(api2);

const weatherAPI = new WeatherAPI(api1Adapter);
console.log(weatherAPI.getWeather()); // Outputs weather data from API 1

weatherAPI.adapter = api2Adapter;
console.log(weatherAPI.getWeather()); // Outputs weather data from API 2

В этом примере у нас есть два внешних API погоды (WeatherAPI1 и WeatherAPI2), которые действуют как Adaptees.

WeatherAPI представляет целевой интерфейс, а адаптеры API1Adapter и API2Adapter служат адаптерами для каждого API.

WeatherAPI предоставляет унифицированный метод getWeather, который внутри использует соответствующий адаптер для извлечения данных из соответствующего API. Используя шаблон адаптера, приложение может легко интегрироваться с несколькими внешними API без необходимости значительных изменений.

Заключение

Шаблон адаптера — ценный инструмент для упрощения задач интеграции в JavaScript.

Выступая в качестве моста между несовместимыми интерфейсами, он позволяет объектам или компонентам работать вместе без изменения их исходного кода. Примеры, приведенные в этом сообщении блога, иллюстрируют, как шаблон адаптера можно применять для адаптации устаревшего кода или интеграции внешних API, предоставляя унифицированный интерфейс и способствуя повторному использованию кода.

Включение шаблона адаптера в ваши проекты JavaScript может значительно повысить гибкость и модульность, делая задачи интеграции более плавными и управляемыми.

Спасибо за прочтение 🙏😇



Советы и рекомендации по веб-разработке | Узнайте о советах и ​​рекомендациях по веб-разработке | Patreon
«Советы и рекомендации по веб-разработке
 — ваш главный ресурс, посвященный последним тенденциям, практическим советам и инновациям…patreon.com»



Свяжись со мной 👇