Pelajari bagaimana pola Adapter menghubungkan antarmuka yang tidak kompatibel dengan contoh TypeScript, React, dan Node.js untuk memodernkan integrasi dan kode warisan.
December 19, 2025 (4mo ago) — last updated February 10, 2026 (2mo ago)
Pola Adapter: Contoh TypeScript, React & Node.js
Pelajari bagaimana pola Adapter menghubungkan antarmuka yang tidak kompatibel dengan contoh TypeScript, React, dan Node.js untuk memodernkan integrasi dan kode warisan.
← Back to blog
Adapter Pattern: TypeScript, React & Node.js Examples
Ringkasan: Pelajari bagaimana pola Adapter menghubungkan antarmuka yang tidak kompatibel di TypeScript, React, dan Node.js dengan contoh praktis dan nyata.
Introduction
Pernahkah Anda memiliki pustaka yang baik atau modul warisan yang tidak cocok dengan sisa sistem Anda? Ini seperti mencoba memasang adaptor Eropa ke soket Amerika Utara — keduanya berfungsi, tetapi antarmukanya tidak cocok. Pola Adapter menyelesaikan itu dengan menerjemahkan satu antarmuka ke antarmuka lain sehingga Anda dapat menggunakan kembali kode yang ada tanpa mengubahnya.
Panduan ini menjelaskan pola tersebut, menampilkan contoh TypeScript dan React, serta mendemonstrasikan adapter Node.js yang memodernkan modul berbasis callback. Fokusnya pada teknik praktis yang bisa Anda terapkan segera untuk membersihkan integrasi dan meningkatkan kemampuan pengujian.
Why the Adapter Pattern Matters
Pola Adapter adalah pola struktural yang membungkus objek yang tidak kompatibel dan mengekspos antarmuka yang diharapkan oleh kode Anda. Pola ini pertama kali didokumentasikan oleh Gang of Four pada tahun 19941. Adapter sangat penting untuk mengintegrasikan API pihak ketiga, memodernkan kode warisan, dan menyatukan sumber data yang berbeda.
Skenario umum di mana adapter membantu:
- Mengintegrasikan API pihak ketiga yang mengembalikan bentuk data berbeda.
- Memodernkan API callback warisan agar bekerja dengan async/await.
- Menyatukan beberapa sumber data menjadi satu antarmuka untuk komponen UI.
Menggunakan adapter menjaga logika bisnis tetap bersih dan terlepas dari sistem eksternal.
Adapter Pattern at a Glance
| 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 |
Structure and Roles
Pola ini memiliki empat peran:
- The Client — kode yang membutuhkan antarmuka tertentu.
- The Target Interface — kontrak yang diharapkan oleh Client.
- The Adaptee — kelas atau modul yang tidak kompatibel tetapi memiliki fungsionalitas yang dibutuhkan.
- The Adapter — mengimplementasikan Target dan mendelegasikan ke Adaptee, menerjemahkan panggilan sesuai kebutuhan.
Dua gaya adapter yang umum:
- Object adapter (komposisi): Adapter memegang instance dari Adaptee. Ini adalah pendekatan yang paling fleksibel.
- Class adapter (inheritance): Adapter mewarisi dari Adaptee dan mengimplementasikan Target. Ini membutuhkan pewarisan berganda dan kurang umum dalam JavaScript dan TypeScript modern.
Practical Example: TypeScript + React
Bayangkan sebuah dasbor yang menerima profil pengguna dari dua layanan dengan bentuk respons yang berbeda. Tanpa adapter, komponen akan dipenuhi logika kondisional.
Incompatible API shapes
// Data from UserServiceA
interface UserA {
userId: number;
fullName: string;
emailAddress: string;
}
// Data from UserServiceB
interface UserB {
id: string;
name: string;
contact: {
email: string;
};
}
Target interface our app expects
interface UnifiedUser {
id: string;
name: string;
email: string;
}
TypeScript adapters
// 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,
};
}
Sentralisasi transformasi menjaga komponen tetap bersih dan tangguh. Jika sebuah API mengganti nama field, hanya adapter yang berubah.
React component that consumes unified data
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>
);
};
Komponen ini bergantung pada satu bentuk yang dapat diprediksi, sehingga pengujian dan penggunaan ulang menjadi lebih mudah.
Example: Modernizing a Callback-Based Node.js Module
Modul warisan sering menggunakan error-first callback. Alih-alih memodifikasi modul yang stabil, buat adapter yang mengekspos API berbasis Promise.
Legacy adaptee (do not modify)
// 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;
Adapter that returns a 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;
Ini mencerminkan perilaku util.promisify Node tetapi menjaga logika adaptasi tetap eksplisit dan mudah diuji3.
Using the adapter in application code
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();
Ini menjaga kode warisan tetap utuh sambil memberikan antarmuka modern ke seluruh basis kode Anda.
When to Use an Adapter
Gunakan adapter ketika dua komponen tidak dapat berkomunikasi langsung karena antarmukanya berbeda. Skenario tipikal:
- Mengintegrasikan API pihak ketiga yang input atau outputnya tidak sesuai dengan model Anda.
- Membungkus API callback warisan agar bekerja dengan async/await.
- Mendukung beberapa sumber data dengan format berbeda dengan membuat satu adapter per sumber.
Kapan tidak menggunakan adapter:
- Jika Anda mengendalikan kedua sistem dan refaktor kecil akan menyelesaikan ketidakcocokan, lebih baik refaktor langsung.
- Jika tujuan Anda adalah menyederhanakan subsistem yang kompleks, pertimbangkan Facade. Facade menawarkan titik masuk tingkat tinggi yang disederhanakan; Adapter fokus hanya pada kompatibilitas.
Quick decision checklist
| 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 |
Testing and Performance
Adapter meningkatkan kemampuan pengujian dengan memisahkan logika inti dari sistem eksternal. Anda dapat memalsukan antarmuka adapter untuk menguji komponen secara terisolasi, dan menguji adapter secara terpisah untuk memverifikasi logika terjemahan.
Overhead performa dari adapter minimal — biasanya satu pemanggilan fungsi ekstra — dan dapat diabaikan dibanding I/O jaringan atau kueri basis data. Untuk sebagian besar aplikasi web, manfaat pemeliharaan dan pemisahan jauh lebih besar daripada biaya kecil tersebut. JavaScript tetap menjadi bahasa yang paling banyak digunakan dalam Stack Overflow Developer Survey, yang menyoroti seberapa sering pengembang menghadapi pekerjaan integrasi yang diselesaikan adapter4.
Frequently asked questions
Q: What problem does the Adapter pattern solve?
A: It resolves interface incompatibilities by translating calls from a client into calls the adaptee understands so you can reuse code without changing it.
Q: How does an Adapter help with legacy code?
A: An Adapter wraps legacy modules and exposes a modern interface, letting you integrate old, stable code into new applications without risky rewrites.
Q: When should I choose an Adapter over other patterns?
A: Choose an Adapter when you need compatibility between two mismatched interfaces. If you want to simplify a whole subsystem, use a Facade instead.
Further reading and internal links
- 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
Di Clean Code Guy, kami membantu tim menerapkan pola desain praktis yang mengubah basis kode yang rapuh dan kompleks menjadi aset yang tangguh, mudah diuji, dan menyenangkan untuk dikerjakan. Jika Anda sedang bergulat dengan sistem warisan atau integrasi yang sulit, Clean Code Audits kami dapat memberi Anda peta jalan yang jelas dan dapat ditindaklanjuti menuju arsitektur yang lebih sehat. Pelajari bagaimana kami dapat membantu Anda mengirimkan kode yang lebih baik, lebih cepat.
AI menulis kode.Anda membuatnya bertahan.
Di era akselerasi AI, kode bersih bukan hanya praktik yang baik — ini adalah perbedaan antara sistem yang berkembang dan codebase yang runtuh di bawah beratnya sendiri.