cv.cm

Pemrograman · Pola desain

Pola Adapter dijelaskan dengan contoh

4 menit baca

Jawaban singkat

Adapter adalah pola desain structural yang mengubah interface sebuah kelas yang ada menjadi interface yang diharapkan kode klien. Adapter membungkus objek yang tidak kompatibel dan menerjemahkan setiap panggilan, misalnya mengubah charge(amountCents) menjadi makePayment("12.50") milik sistem lama. Pola ini memungkinkan Anda memakai ulang komponen pihak ketiga atau legacy tanpa mengubahnya dan tanpa menyebarkan API yang canggung itu ke seluruh kode Anda. Pola ini juga dikenal sebagai Wrapper.

Tujuan

Mengubah interface sebuah kelas menjadi interface lain yang diharapkan klien. Pola Adapter memungkinkan kelas-kelas yang sebelumnya tidak bisa bekerja bersama karena interface tidak kompatibel menjadi dapat bekerja bersama. Pola ini salah satu dari tujuh pola structural dalam buku Gang of Four (GoF).

Masalah yang diselesaikan

Kode toko Anda bekerja dengan interface kecil PaymentGateway: charge(amountCents, currency) mengembalikan hasil dengan flag ok dan ID transaksi. Sekarang bisnis menandatangani kontrak dengan penyedia pembayaran yang SDK-nya tampak sangat berbeda: jumlah berupa string desimal, mata uang menjadi bagian dari objek permintaan, dan error kembali sebagai kode status numerik.

Anda tidak bisa mengubah SDK, dan tidak ingin menulis ulang setiap pemanggil agar memakai bentuk baru. Jika gaya SDK merembes ke checkout, refund, dan laporan, berganti penyedia nanti akan mahal. Adapter menjaga penerjemahan di satu kelas.

Struktur dan peserta

PesertaTanggung jawabDalam contoh
TargetInterface yang diharapkan klien.PaymentGateway
AdapteeKelas yang sudah ada dengan interface yang tidak kompatibel.LegacyPayClient
AdapterMengimplementasikan Target dan menerjemahkan panggilan ke Adaptee.LegacyPayAdapter
KlienHanya bekerja dengan interface Target.checkout

Implementasi TypeScript

// Target: what our code expects.
interface PaymentGateway {
  charge(amountCents: number, currency: string): { ok: boolean; id: string };
}

// Adaptee: a third-party SDK we cannot change.
class LegacyPayClient {
  makePayment(req: { amount: string; curr: string }): { status: number; ref: string } {
    return { status: req.curr === "USD" ? 200 : 422, ref: `TX-${req.amount}` };
  }
}

// Adapter: implements the Target by translating to the Adaptee.
class LegacyPayAdapter implements PaymentGateway {
  constructor(private client: LegacyPayClient) {}

  charge(amountCents: number, currency: string) {
    const res = this.client.makePayment({
      amount: (amountCents / 100).toFixed(2),
      curr: currency.toUpperCase(),
    });
    return { ok: res.status === 200, id: res.ref };
  }
}

// Client code knows nothing about LegacyPayClient.
function checkout(gateway: PaymentGateway, totalCents: number): string {
  const result = gateway.charge(totalCents, "usd");
  return result.ok ? `paid ${result.id}` : "payment failed";
}

console.log(checkout(new LegacyPayAdapter(new LegacyPayClient()), 1250)); // paid TX-12.50

Berganti ke penyedia lain nanti berarti menulis satu adapter lagi. checkout dan setiap pemanggil lain tetap sama, dan pengujian dapat meneruskan PaymentGateway tiruan.

Object adapter versus class adapter

Buku GoF menjelaskan dua bentuk:

BentukCara kerjaCatatan
Object adapterMenyimpan referensi ke adaptee dan mendelegasikan ke sana (composition).Bekerja dengan adaptee dan semua subkelasnya. Pilihan umum di TypeScript, Java, dan C#.
Class adapterMewarisi dari adaptee dan mengimplementasikan interface target.Membutuhkan inheritance dari adaptee, sehingga hanya mengadaptasi satu kelas itu. Umum di C++ dengan multiple inheritance.

Di TypeScript dan JavaScript, mengadaptasi fungsi sering sudah cukup. Node.js menyertakan adapter kecil semacam itu: util.promisify() mengubah fungsi bergaya callback menjadi fungsi yang mengembalikan promise.

import { promisify } from "node:util";

// Adaptee: old callback style (error first, then result).
function readConfig(name: string, cb: (err: Error | null, value?: string) => void): void {
  setTimeout(() => cb(null, `config for ${name}`), 10);
}

// Adapted: the same function, now with a promise-based interface.
const readConfigAsync = promisify(readConfig);
console.log(await readConfigAsync("app")); // config for app

Kapan memakainya, dan kapan tidak

Gunakan pola Adapter ketika:

  • Anda ingin memakai kelas yang sudah ada, tetapi interface-nya tidak cocok dengan yang dibutuhkan kode Anda.
  • Anda mengintegrasikan pustaka pihak ketiga atau sistem legacy dan ingin menjaga API-nya di tepi program Anda.
  • Anda ingin beberapa implementasi berbeda, seperti dua penyedia pembayaran, di balik satu interface.

Hindari ketika:

  • Anda memiliki kedua sisi. Mengubah salah satu interface biasanya lebih sederhana daripada menambah lapisan penerjemah.
  • Adapter harus memalsukan bagian besar dari interface target yang sebenarnya tidak bisa didukung adaptee. Itu menyembunyikan ketidakcocokan nyata alih-alih menyelesaikannya.

Pemakaian di dunia nyata

  • Node.js: util.promisify() mengadaptasi fungsi berbasis callback ke promise.
  • Java: Arrays.asList() memungkinkan array dipakai lewat interface List, dan InputStreamReader memungkinkan InputStream berbasis byte dibaca sebagai Reader berbasis karakter.
  • Pustaka database dan ORM memakai satu adapter atau driver per database, sehingga API query yang sama bekerja dengan PostgreSQL, MySQL, atau SQLite.
  • Kode frontend sering membungkus API browser atau SDK analitik di balik interface kecil khusus proyek karena alasan yang sama.

Pola terkait

PolaPerbedaannya dengan Adapter
DecoratorMempertahankan interface yang sama dan menambah perilaku. Adapter mengubah interface dan tidak menambah perilaku baru.
ProxyMempertahankan interface yang sama dan mengontrol akses ke objek (lazy loading, caching, izin).
FacadeMendefinisikan interface baru yang lebih sederhana untuk seluruh subsistem berisi banyak kelas. Adapter membuat satu interface yang ada cocok dengan interface lain yang ada.
BridgeDirancang sejak awal agar abstraksi dan implementasinya bisa bervariasi terpisah. Adapter biasanya ditambahkan belakangan agar kelas yang ada cocok.

FAQ

Apa beda pola Adapter dan pola Facade?

Adapter membuat satu kelas yang ada cocok dengan interface yang sudah diharapkan klien. Facade membuat interface baru yang lebih sederhana di depan subsistem kompleks berisi banyak kelas.

Apa beda pola Adapter dan pola Decorator?

Keduanya membungkus objek. Decorator mempertahankan interface yang sama dan menambah perilaku; adapter mengubah interface agar objek yang dibungkus bisa dipakai di tempat yang sebelumnya tidak mungkin.

Apakah Adapter sama dengan wrapper?

Wrapper adalah nama lain Adapter dalam buku GoF. Dalam percakapan sehari-hari, "wrapper" juga dipakai untuk decorator dan proxy, jadi tujuannya yang menentukan pola mana yang dimaksud.

Bisakah satu adapter mengadaptasi beberapa kelas?

Object adapter dapat bekerja dengan sebuah adaptee dan semua subkelasnya. Mengadaptasi kelas yang tidak berhubungan biasanya berarti satu adapter per kelas, semuanya mengimplementasikan interface target yang sama.

Semua tutorial pemrograman