December 19, 2025 (3mo 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. Адаптер — реалізує Цільовий інтерфейс і делегує виклики Adaptee, перекладаючи їх за потреби.

Два поширені стилі адаптера:

  • Об’єктний адаптер (композиція): Адаптер містить екземпляр Adaptee. Це найгнучкіший підхід.
  • Класовий адаптер (успадкування): Адаптер наслідує Adaptee і реалізує Target. Це вимагає множинного успадкування і менш поширене в сучасному 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 на основі колбеків

Застарілі модулі часто використовують колбеки з першою аргументом помилку (error-first callbacks). Замість зміни стабільного модуля створіть адаптер, який надає API на основі Promise.

Застарілий adaptee (не змінюйте)

// 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.
  • Підтримка кількох джерел даних з різними форматами шляхом створення одного адаптера на джерело.

Коли не варто використовувати адаптер:

  • Якщо ви контролюєте обидві системи й невелике рефакторинг вирішить невідповідність — віддайте перевагу прямому рефакторингу.
  • Якщо ваша мета — спростити складну підсистему, розгляньте Фасад замість цього. Фасад пропонує спрощений, високорівневий вхід; Адаптер зосереджений лише на сумісності.

Швидкий чекліст для рішення

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.

Часті запитання

Q: Яку проблему вирішує патерн Адаптер?

A: Він вирішує несумісність інтерфейсів, перекладаючи виклики з клієнта в виклики, які розуміє adaptee, щоб ви могли повторно використовувати код без його змін.

Q: Як адаптер допомагає з застарілим кодом?

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

Q: Коли обрати Адаптер замість інших патернів?

A: Обирайте Адаптер, коли вам потрібна сумісність між двома невідповідними інтерфейсами. Якщо ви хочете спростити цілу підсистему, краще використати Фасад.

Додаткове читання й внутрішні посилання


At Clean Code Guy, we help teams implement practical design patterns that turn brittle, complex codebases into assets that are resilient, testable, and a pleasure to work on. If you’re wrestling with a legacy system or tricky integrations, our Clean Code Audits can give you a clear, actionable roadmap to a healthier architecture. Learn how we can help you ship better code, faster.

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
🙋🏻‍♂️

ШІ пише код.
Ви робите його довговічним.

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