December 19, 2025 (9mo ago) — last updated August 7, 2026 (1mo ago)

Паттерн «Адаптер» — TypeScript, React и Node.js

Как паттерн «Адаптер» переводит несовместимые интерфейсы — практические примеры на TypeScript, React и Node.js для модернизации интеграций.

← Back to blog
Cover Image for Паттерн «Адаптер» — TypeScript, React и Node.js

Быстрое практическое руководство по паттерну «Адаптер»: что это даёт, когда применять и как реализовать на TypeScript, React и Node.js, чтобы упростить интеграции и модернизировать унаследованный код.

Паттерн «Адаптер» — TypeScript, React и Node.js

Краткое содержание: Практическое руководство по использованию паттерна «Адаптер» для объединения несовместимых интерфейсов — примеры на TypeScript, React и Node.js.

Введение

Бывало у вас отличная библиотека или унаследованный модуль, который не вписывается в остальную систему? Паттерн «Адаптер» переводит один интерфейс в другой, позволяя повторно использовать существующий код без его изменения. В этой статье показаны простые, применимые техники и примеры на TypeScript, React и Node.js, которые помогут почистить интеграции и улучшить тестируемость.

Почему паттерн «Адаптер» важен

Паттерн «Адаптер» — структурный паттерн, который оборачивает несовместимый объект и предоставляет интерфейс, ожидаемый вашим компонентам. Он был впервые описан Gang of Four в 1994 году1. Адаптеры особенно полезны при интеграции сторонних API, модернизации унаследованного кода и унификации разнородных источников данных.

Типичные сценарии использования:

  • Интеграция сторонних API с разными форматами данных.
  • Модернизация унаследованных API на колбэках для работы с async/await.
  • Унификация нескольких источников данных в единый интерфейс для UI‑компонентов.

Правильное использование адаптера держит бизнес‑логику чистой и отделённой от внешних систем.

Основная идея и роли

Паттерн реализует четыре роли:

  1. Клиент — код, которому нужен определённый интерфейс.
  2. Целевой интерфейс — контракт, ожидаемый клиентом.
  3. Адаптируемый (Adaptee) — несовместимый класс или модуль с нужной функциональностью.
  4. Адаптер — реализует целевой интерфейс и делегирует вызовы адаптируемому, при необходимости преобразуя данные.

Два распространённых стиля адаптера:

  • Объектный адаптер (композиция): адаптер хранит экземпляр адаптируемого — гибкий и предпочтительный в JavaScript/TypeScript.
  • Классовый адаптер (наследование): требует множественного наследования и редко используется в современном JS/TS.

Быстрая сводка

ConceptDescription
TypeStructural
Primary intentAllow objects with incompatible interfaces to work together
Core ideaWrap the adaptee to expose the target interface
Key problem solvedReuse existing classes without changing their source code
Common use casesThird‑party libraries, legacy code, multiple data sources

Практический пример: TypeScript + React

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

Несовместимые формы API

// Data from UserServiceA
interface UserA {
  userId: number;
  fullName: string;
  emailAddress: string;
}

// Data from UserServiceB
interface UserB {
  id: string;
  name: string;
  contact: {
    email: string;
  };
}

Целевой интерфейс приложения

interface UnifiedUser {
  id: string;
  name: string;
  email: string;
}

Адаптеры в TypeScript

// Adapter for UserServiceA
function adaptUserA(userA: UserA): UnifiedUser {
  return {
    id: userA.userId.toString(),
    name: userA.fullName,
    email: userA.emailAddress,
  };
}

// Adapter for UserServiceB
function adaptUserB(userB: UserB): UnifiedUser {
  return {
    id: userB.id,
    name: userB.name,
    email: userB.contact.email,
  };
}

React‑компонент, использующий объединённые данные

interface UserProfileProps {
  user: UnifiedUser;
}

const UserProfile: React.FC<UserProfileProps> = ({ user }) => {
  return (
    <div>
      <h2>{user.name}</h2>
      <p>ID: {user.id}</p>
      <p>Email: {user.email}</p>
    </div>
  );
};

Этот компонент опирается на одну предсказуемую форму данных, что упрощает тестирование и повторное использование.

Пример: модернизация модуля Node.js на колбэках

Унаследованные модули часто используют колбэки с первым параметром‑ошибкой. Вместо изменения устойчивого модуля создайте адаптер, который предоставляет API на основе Promise.

Наследуемый модуль (не изменять)

// legacyFileProcessor.js
const fs = require('fs');

class LegacyFileProcessor {
  processFile(filePath, callback) {
    fs.readFile(filePath, 'utf8', (err, data) => {
      if (err) {
        return callback(err, null);
      }
      const processedContent = data.toUpperCase();
      callback(null, processedContent);
    });
  }
}

module.exports = LegacyFileProcessor;

Адаптер, возвращающий Promise

// FileProcessorAdapter.js
const LegacyFileProcessor = require('./legacyFileProcessor');

class FileProcessorAdapter {
  constructor() {
    this.legacyProcessor = new LegacyFileProcessor();
  }

  processFile(filePath) {
    return new Promise((resolve, reject) => {
      this.legacyProcessor.processFile(filePath, (err, data) => {
        if (err) return reject(err);
        resolve(data);
      });
    });
  }
}

module.exports = FileProcessorAdapter;

Это похоже на поведение util.promisify в Node.js, но сохраняет логику адаптации явной и тестируемой3.

Использование адаптера в приложении

const FileProcessorAdapter = require('./FileProcessorAdapter');
const fileProcessor = new FileProcessorAdapter();

async function handleFileProcessing() {
  try {
    console.log('Processing file with modern async/await...');
    const content = await fileProcessor.processFile('my-file.txt');
    console.log('Processed Content:', content);
  } catch (error) {
    console.error('An error occurred:', error);
  }
}

handleFileProcessing();

Так вы оставляете унаследованный код нетронутым и предоставляете остальной части системы современный интерфейс.

Когда использовать адаптер

Используйте адаптер, когда компоненты не могут напрямую общаться из‑за различий в интерфейсах. Сценарии:

  • Третья сторона возвращает данные в формате, отличном от ваших моделей.
  • Вам нужно обёрнуть унаследованные API на колбэках для async/await.
  • Несколько источников с разной структурой данных — создайте адаптер для каждого источника.

Когда не стоит использовать адаптер:

  • Вы контролируете обе стороны и небольшой рефакторинг решит проблему — лучше рефакторить.
  • Нужно упростить подсистему целиком — фасад (Facade) будет уместнее.

Краткий чеклист

SituationUse Adapter?Why
Need to use a third‑party library with incompatible APIYesYou can’t change the library
Control both sides and change is smallNoRefactor directly
Need a simplified high‑level interfaceNoUse Facade
Migrating legacy systems incrementallyYesWrap old components
Multiple differently structured data sourcesYesAdapters unify them

Тестирование и производительность

Адаптеры улучшают тестируемость: вы можете мокать интерфейс адаптера для изолированного тестирования компонентов и отдельно тестировать логику преобразования. Накладные расходы минимальны — обычно один дополнительный вызов — и несущественны по сравнению с сетевым вводом/выводом или запросами в базу данных. JavaScript остаётся самым используемым языком согласно опросу разработчиков, что подчёркивает частые интеграционные задачи, решаемые адаптерами4.

Часто задаваемые вопросы

В: Какую проблему решает паттерн «Адаптер»?

А: Он устраняет несовместимость интерфейсов, переводя вызовы от клиента в вызовы, которые понимает адаптируемый объект, что позволяет переиспользовать код без его изменения.

В: Как адаптер помогает с унаследованным кодом?

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

В: Когда выбирать адаптер вместо других паттернов?

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

Краткие практические советы

  • Держите адаптеры простыми: одна ответственность — привести формат данных к ожидаемому виду.
  • Тестируйте адаптеры отдельно: проверяйте все возможные варианты преобразований.
  • Централизуйте адаптации, чтобы изменения API трогали только адаптеры, а не компоненты.

Дополнительная литература


1.
Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides, Design Patterns: Elements of Reusable Object‑Oriented Software (Addison‑Wesley, 1994). [https://en.wikipedia.org/wiki/Design_Patterns_(book)](https://en.wikipedia.org/wiki/Design_Patterns_(book))
2.
Adapter pattern overview and examples: https://www.geeksforgeeks.org/adapter-pattern/
3.
Node.js documentation for util.promisify, a common approach to convert callbacks to Promises: https://nodejs.org/api/util.html#utilpromisifyoriginal
4.
Stack Overflow Developer Survey 2023: prevalence of JavaScript and web technologies used by developers: https://survey.stackoverflow.co/2023/
← Back to blog
🙋🏻‍♂️

ИИ пишет код.
Вы делаете его долговечным.

В эпоху ускорения ИИ чистый код — это не просто хорошая практика — это разница между системами, которые масштабируются, и кодовыми базами, которые рушатся под собственным весом.