December 19, 2025 (4mo ago) — last updated February 10, 2026 (2mo 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 в 19941. Адаптеры незаменимы при интеграции сторонних API, модернизации унаследованного кода и унификации разнородных источников данных.

Типичные сценарии, где помогают адаптеры:

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

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

Паттерн «Адаптер» в кратце

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

Структура и роли

У паттерна четыре роли:

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

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

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

Практический пример: 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,
  };
}

Централизация преобразований сохраняет компоненты чистыми и устойчивыми. Если API переименует поле, менять придётся только адаптер.

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, но сохраняет логику адаптации явной и тестируемой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, чьи входные или выходные данные не соответствуют вашим моделям.
  • Обёртка унаследованных API на колбэках, чтобы они работали с async/await.
  • Поддержка нескольких источников данных с разными форматами посредством создания отдельного адаптера для каждого источника.

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

  • Если вы контролируете обе стороны и небольшая рефакторинг решит рассогласование, предпочитайте прямой рефакторинг.
  • Если цель — упростить сложную подсистему, рассмотрите вместо этого фасад (Facade). Фасад предоставляет упрощённую, высокоуровневую точку входа; адаптер фокусируется только на совместимости.

Краткий чеклист принятия решения

SituationUse Adapter?Why
Need to use a third-party library with incompatible APIYesYou can’t change the library, so adapt to it
Control both sides and change is smallNoRefactor directly to avoid extra indirection
Need a simplified high-level interface to a complex systemNoFacade is a better fit
Migrating legacy systems incrementallyYesWrap old components to match new interfaces
Multiple differently structured data sourcesYesAdapters unify them into one shape

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

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

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

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

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

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

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

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

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

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

Дополнительная литература и внутренние ссылки


В Clean Code Guy мы помогаем командам внедрять практические шаблоны проектирования, которые превращают хрупкие, сложные кодовые базы в активы, устойчивые, тестируемые и приятные для работы. Если вы боретесь с унаследованной системой или сложными интеграциями, наши Clean Code Audits могут дать вам чёткую, применимую дорожную карту к более здоровой архитектуре. Узнайте, как мы можем помочь вам выпускать лучший код быстрее.

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, showing the prevalence of JavaScript and web technologies that commonly require integration work: https://survey.stackoverflow.co/2023/
← Back to blog
🙋🏻‍♂️

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

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