Facade Pattern — Design Patterns

Facade menyediakan antarmuka sederhana di atas subsystem yang kompleks. Client cukup panggil satu method, dibalik layar ada banyak step. Problem yang…

Facade Pattern memberi satu pintu yang sederhana di depan beberapa bagian (subsystem) yang harus dipanggil dengan urutan tertentu. Pemanggil cukup memanggil satu function, dan facade yang mengatur urutannya di belakang. Bayangin resepsionis hotel. Kamu bilang "check-in, dua malam". Resepsionis yang mengecek reservasi, mencatat KTP, menyiapkan kartu kamar, dan mengabari housekeeping. Kamu gak perlu tahu ada empat bagian, apalagi urutannya. Tapi resepsionis gak menentukan harga kamar: dia cuma menyampaikan harga dari bagian reservasi.

🧪 Tebakan cepat: aturan baru "member gold dapat potongan 15%" ditaruh di dalam facade KurirKancil.kirim. Halaman cek ongkir tetap memanggil hitungOngkir("kilat", 2) langsung. Untuk member gold, berapa harga yang tampil di cek ongkir, dan berapa yang ditagih saat checkout? Tebak dulu, jawabannya ada di bagian "Facade yang tumbuh jadi god object".

Masalahnya: tiap pemanggil harus tahu empat langkah

Mengirim paket di Kurir Kancil melibatkan subsystem dari lesson-lesson sebelumnya: hitungOngkir dan PROMO (Strategy), resiCounter (Singleton), dan tracker (Observer). Halaman checkout, aplikasi, dan panel admin masing-masing menulis langkah yang sama:

// checkout.js, aplikasi.js, dan admin.js menulis ini masing-masing
const harga = PROMO.diskon10(hitungOngkir("kilat", 2))
const resi = resiCounter.next()
const parcel = { resi, service: "kilat", kg: 2, harga, status: "dijemput" }
tracker.emit("status", parcel) // supaya muncul di halaman lacak

Tiga salinan dari empat langkah. Salinan di admin lupa baris tracker.emit? Kiriman KCL-0003 tetap dibuat dan ditagih, tapi halaman lacak menjawab "tidak ditemukan", tanpa error. Ada langkah baru, misalnya mendaftarkan tiap kiriman ke mitra asuransi? Tiga file diubah, dan satu pasti ketinggalan.

Facade: satu pintu untuk mengirim paket

const KurirKancil = {
  kirim(service, kg, promo = "tanpa") {
    const harga = PROMO[promo](hitungOngkir(service, kg)) // dicek dulu, bisa throw
    const parcel = { resi: resiCounter.next(), service, kg, harga, status: "dijemput" }
    tracker.emit("status", parcel)
    return parcel
  },
}

// checkout.js, aplikasi.js, admin.js cukup menulis:
const p = KurirKancil.kirim("kilat", 2, "diskon10")
console.log(p.resi + " Rp " + p.harga) // KCL-0001 Rp 32400
tanpa facade dengan facade
yang harus dikenal pemanggil hitungOngkir, PROMO, resiCounter, tracker, dan urutannya KurirKancil.kirim
langkah baru (asuransi) diubah di 3 file 1 baris di kirim
langkah yang bisa terlupa di tiap salinan gak ada, cuma satu salinan

Kamu sudah sering memakai facade: $("#resi").addClass("aktif") di jQuery menyembunyikan detail DOM di balik satu panggilan, fetch(url) menyembunyikan DNS, koneksi, dan header HTTP, dan Laravel bahkan menamai fiturnya Facades: Cache::get("key") atau Mail::to($user)->send(...) adalah satu nama pendek untuk layanan yang di belakangnya punya driver dan konfigurasi.

Facade yang tumbuh jadi god object

Facade gampang jadi tempat menaruh apa saja, karena semua orang sudah memanggilnya. Misalnya aturan member gold:

kirim(service, kg, promo = "tanpa", member = "biasa") {
  let harga = PROMO[promo](hitungOngkir(service, kg))
  if (member === "gold") harga = harga * 0.85 // aturan harga di dalam facade
  // ...
}

Halaman cek ongkir memanggil hitungOngkir("kilat", 2) dan menampilkan Rp 36.000. Saat checkout, member gold ditagih 36.000 × 0,85 = Rp 30.600. Itu jawaban tebakannya: dua harga untuk barang yang sama, karena aturannya disembunyikan di tempat yang gak dilewati semua jalur. Tempatnya di PROMO, misalnya PROMO.gold = (price) => price * 0.85, yang juga bisa dipanggil halaman cek ongkir.

Setahun kemudian, facade seperti ini bisa punya 30 method: kirim, refund, gaji kurir, laporan bulanan, email promo. Itu disebut god object: satu object yang tahu dan mengerjakan semuanya. Tiap tim mengubah file yang sama, dan tiap test checkout harus menyiapkan semua subsystem. Facade yang sehat tetap tipis: cuma mengatur urutan, tanpa aturan bisnis sendiri. Kalau mulai gemuk, pecah per bidang: Pengiriman, Laporan, Penggajian.

Facade, adapter, dan decorator

Latihan: VideoConverter

Latihan nanti memakai bentuk yang sama untuk konversi video. Starter sudah berisi tiga subsystem: readFile(path) mengembalikan "raw:" + path, decode(raw) mengembalikan "decoded:" + raw, dan encode(decoded, format) mengembalikan "encoded:" + decoded + ":" + format. Kamu mengisi VideoConverter.convert(path, format) supaya memanggil ketiganya berurutan, lalu mengembalikan hasil encode:

langkah hasil untuk convert("movie.avi", "mp4")
readFile("movie.avi") raw:movie.avi
decode(...) decoded:raw:movie.avi
encode(..., "mp4") encoded:decoded:raw:movie.avi:mp4

Console-nya jadi dua baris: encoded:decoded:raw:movie.avi:mp4 dan encoded:decoded:raw:clip.mov:webm. Untuk convert("intro.mkv", "ogg"), hasilnya encoded:decoded:raw:intro.mkv:ogg.

Kapan gak perlu facade

⚠️ Jebakan yang sering ketemu

TL;DR: Facade memberi satu pintu sederhana di depan beberapa subsystem yang harus dipanggil berurutan. KurirKancil.kirim(service, kg, promo) menghitung harga, mengambil resi, dan mengabari tracker, jadi checkout, aplikasi, dan admin gak perlu menyalin empat langkah (dan gak bisa lupa satu). Facade cuma mengatur urutan: rumus tetap di subsystem, dan subsystem tetap boleh dipanggil langsung. Aturan bisnis di dalam facade membuat dua harga (cek ongkir Rp 36.000, ditagih Rp 30.600), dan facade yang menelan semuanya jadi god object. Adapter menerjemahkan satu bentuk, decorator membungkus dengan cara panggil yang sama, facade menyederhanakan banyak bagian. Di latihan: return encode(decode(readFile(path)), format).

Yang akan kamu pelajari