Adapter Pattern — Design Patterns

Adapter menerjemahkan satu interface ke interface lain yang diharapkan client. Seperti colokan listrik Eropa → adapter → colokan Indonesia: alatnya sama, cara…

Adapter Pattern adalah function (atau object) kecil yang menerjemahkan bentuk data atau method milik pihak lain ke bentuk yang dipakai kodemu, tanpa mengubah keduanya. Bayangin charger laptop yang kamu beli di luar negeri. Kakinya gak cocok dengan stop kontak di rumah. Kamu gak membongkar charger-nya dan gak mengganti stop kontaknya. Kamu pasang adaptor colokan di antaranya. Listriknya sama, cuma bentuk sambungannya yang diterjemahkan.

🧪 Tebakan cepat: kurir mitra Merpati mengirim data paket { awb: "MTR-0088", weightGrams: 1500 }. Function label(parcel) milik Kurir Kancil membaca parcel.resi dan parcel.kg. Apa yang tercetak kalau data Merpati langsung dikirim ke label? Tebak dulu, jawabannya ada di bagian "Masalahnya: nama field yang beda".

Masalahnya: nama field yang beda

Seluruh kode Kurir Kancil memakai bentuk { resi, kg }. Merpati memakai nama dan satuannya sendiri:

function label(parcel) {
  return parcel.resi + " · " + parcel.kg + " kg"
}

function hitungOngkir(kg) {
  return kg * 10000 // reguler
}

const fromMerpati = { awb: "MTR-0088", weightGrams: 1500 }

console.log(label(fromMerpati))          // undefined · undefined kg
console.log(hitungOngkir(fromMerpati.kg)) // NaN

Object Merpati gak punya resi dan kg, jadi yang terbaca undefined: label undefined · undefined kg. Itu jawaban tebakannya. undefined * 10000 menghasilkan NaN, dan gak ada error yang memperingatkan.

Menambal tiap function dengan parcel.resi ?? parcel.awb membuat semua function harus tahu format Merpati. Mitra kedua berarti tambalan kedua di tiap tempat.

Adapter: terjemahkan di pintu masuk

function adaptMerpati(p) {
  return { resi: p.awb, kg: p.weightGrams / 1000 }
}

const parcel = adaptMerpati(fromMerpati)
console.log(label(parcel))                   // MTR-0088 · 1.5 kg
console.log("Rp " + hitungOngkir(parcel.kg)) // Rp 15000
Merpati Kurir Kancil
awb: "MTR-0088" resi: "MTR-0088"
weightGrams: 1500 kg: 1.5

Adapter juga bisa membungkus object yang method-nya beda nama. Kode baru memanggil getFullName(), object lama cuma punya getName(): adapter-nya object baru dengan getFullName() { return oldUser.getName() }.

Adapter yang diam-diam mengubah arti

Adapter seharusnya cuma mengubah bentuk. Lihat yang terjadi kalau dia ikut membulatkan berat dengan Math.round(p.weightGrams / 1000):

weightGrams / 1000 Math.round ongkir sebenarnya ongkir tertagih
1500 1.5 2 15000 20000
2450 2.45 2 24500 20000
800 0.8 1 8000 10000

Paket 2.450 gram ditagih Rp 20.000, bukan Rp 24.500: Rp 4.500 hilang per paket. Paket 800 gram kelebihan Rp 2.000. Gak ada yang curiga, karena adapter dianggap "cuma menerjemahkan". Kalau Kurir Kancil memang menagih per kilo dibulatkan ke atas (Math.ceil), itu aturan tarif: tulis di perhitungan ongkir, tempat orang mencarinya, bukan di dalam adapter.

Adapter, decorator, dan facade

Latihan: createUserAdapter

Latihan nanti membungkus API lama yang gak boleh diubah. Starter sudah berisi object oldUser dengan getName(), getEmail(), dan getPhone(), plus pemanggilannya. Kamu menulis createUserAdapter(oldUser) yang mengembalikan object baru:

Hasilnya Budi Santoso, [email protected] / 08123456789, dan untuk user kedua [email protected] / 0811. Baca selalu dari object yang dikirim ke parameter, supaya adapter-nya bekerja untuk user siapa pun.

Kapan gak perlu adapter

Adapter pas untuk sisi yang gak bisa kamu ubah: API mitra, library pihak ketiga, sistem lama, atau migrasi bertahap saat API lama dan baru dipakai bersamaan.

⚠️ Jebakan yang sering ketemu

TL;DR: Adapter menerjemahkan bentuk milik pihak lain ke bentuk yang dipakai kodemu, tanpa mengubah keduanya: adaptMerpati(p) mengubah { awb, weightGrams } jadi { resi, kg } dengan weightGrams / 1000, dipanggil sekali di pintu masuk. Tanpa adapter, yang terbaca undefined dan NaN. Adapter cuma mengubah bentuk dan satuan dengan tepat; pembulatan adalah aturan tarif, bukan tugasnya. Untuk object, adapter adalah object baru dengan method yang diharapkan (getFullName, getContact) yang memanggil method lama. Kalau kamu memegang kedua sisi, ganti langsung saja.

Yang akan kamu pelajari