Дізнайтеся, як патерн Адаптер з’єднує несумісні інтерфейси на прикладах з TypeScript, React і Node.js, щоб модернізувати інтеграції та застарілий код.
December 19, 2025 (3mo ago) — last updated February 10, 2026 (2mo ago)
Патерн Адаптер: приклади з TypeScript, React і Node.js
Дізнайтеся, як патерн Адаптер з’єднує несумісні інтерфейси на прикладах з TypeScript, React і Node.js, щоб модернізувати інтеграції та застарілий код.
← Back to blog
Патерн Адаптер: приклади з TypeScript, React і Node.js
Коротко: Дізнайтеся, як патерн Адаптер з’єднує несумісні інтерфейси в TypeScript, React і Node.js на прикладах із реального життя.
Вступ
Коли-небудь у вас був цілком працездатний пакет або застарілий модуль, який просто не вписується в решту вашої системи? Це як намагатися вставити європейський адаптер у північноамериканську розетку — обидва працюють, але їхні інтерфейси не збігаються. Патерн Адаптер вирішує це, переводячи один інтерфейс в інший, щоб ви могли повторно використовувати існуючий код без його змін.
Цей посібник пояснює патерн, показує приклади на TypeScript і React, а також демонструє Node.js-адаптер, який модернізує модулі на основі колбеків. Фокус на практичних техніках, які ви можете застосувати негайно, щоб впорядкувати інтеграції й покращити тестованість.
Чому патерн Адаптер важливий
Патерн Адаптер — це структурний патерн, який обгортає несумісний об’єкт і надає інтерфейс, який очікує ваш код. Він уперше задокументований Gang of Four у 19941. Адаптери необхідні для інтеграції сторонніх API, модернізації застарілого коду та уніфікації розрізнених джерел даних.
Типові сценарії, де адаптери допомагають:
- Інтеграція сторонніх API, які повертають дані в різних форматах.
- Модернізація застарілих API на основі колбеків для роботи з async/await.
- Уніфікація декількох джерел даних в один інтерфейс для UI-компонентів.
Використання адаптера зберігає бізнес-логіку чистою й незалежною від зовнішніх систем.
Патерн Адаптер у короткому огляді
| Concept | Description |
|---|---|
| Type | Structural |
| Primary intent | Allow objects with incompatible interfaces to work together |
| Core idea | Wrap the adaptee to expose the target interface |
| Key problem solved | Reuse existing classes without changing their source code |
| Common use cases | Third-party libraries, legacy code, multiple data sources |
Структура та ролі
Патерн має чотири ролі:
- Клієнт — код, який потребує певного інтерфейсу.
- Цільовий інтерфейс — контракт, який очікує Клієнт.
- Adaptee — несумісний клас або модуль з потрібною функціональністю.
- Адаптер — реалізує Цільовий інтерфейс і делегує виклики 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.
- Підтримка кількох джерел даних з різними форматами шляхом створення одного адаптера на джерело.
Коли не варто використовувати адаптер:
- Якщо ви контролюєте обидві системи й невелике рефакторинг вирішить невідповідність — віддайте перевагу прямому рефакторингу.
- Якщо ваша мета — спростити складну підсистему, розгляньте Фасад замість цього. Фасад пропонує спрощений, високорівневий вхід; Адаптер зосереджений лише на сумісності.
Швидкий чекліст для рішення
| Situation | Use Adapter? | Why |
|---|---|---|
| Need to use a third-party library with incompatible API | Yes | You can’t change the library, so adapt to it |
| Control both sides and change is small | No | Refactor directly to avoid extra indirection |
| Need a simplified high-level interface to a complex system | No | Facade is a better fit |
| Migrating legacy systems incrementally | Yes | Wrap old components to match new interfaces |
| Multiple differently structured data sources | Yes | Adapters unify them into one shape |
Тестування та продуктивність
Адаптери покращують тестованість, відокремлюючи основну логіку від зовнішніх систем. Ви можете змокати інтерфейс адаптера, щоб тестувати компоненти в ізоляції, і тестувати адаптери окремо, щоб перевірити логіку перетворення.
Накладні витрати продуктивності через адаптер мінімальні — зазвичай це один додатковий виклик функції — і є незначними порівняно з мережевими запитами або запитами до бази даних. Для більшості вебдодатків вигоди від підтримки та розв’язування залежностей значно переважують невелику вартість. JavaScript залишається найпопулярнішою мовою згідно з опитуванням розробників Stack Overflow, що підкреслює, як часто розробники стикаються з інтеграційною роботою, яку вирішують адаптери4.
Часті запитання
Q: Яку проблему вирішує патерн Адаптер?
A: Він вирішує несумісність інтерфейсів, перекладаючи виклики з клієнта в виклики, які розуміє adaptee, щоб ви могли повторно використовувати код без його змін.
Q: Як адаптер допомагає з застарілим кодом?
A: Адаптер обгортає застарілі модулі й надає сучасний інтерфейс, дозволяючи інтегрувати старий стабільний код у нові застосунки без ризикових переробок.
Q: Коли обрати Адаптер замість інших патернів?
A: Обирайте Адаптер, коли вам потрібна сумісність між двома невідповідними інтерфейсами. Якщо ви хочете спростити цілу підсистему, краще використати Фасад.
Додаткове читання й внутрішні посилання
- Deep dive on polymorphism vs inheritance: /blog/polymorphism-vs-inheritance
- Strategies for modernizing legacy systems: /blog/modernizing-legacy-systems
- Adapter pattern reference and examples: GeeksforGeeks2
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.
ШІ пише код.Ви робите його довговічним.
В епоху прискорення ШІ чистий код — це не просто хороша практика — це різниця між системами, які масштабуються, та кодовими базами, які руйнуються під власною вагою.