Pemrograman · Pola desain
Penjelasan pola Observer beserta contohnya
Jawaban singkat
Observer adalah pola desain behavioral di mana sebuah objek, yang disebut subject, menyimpan daftar dependen, yang disebut observer, dan memberi tahu mereka secara otomatis saat state-nya berubah. Observer berlangganan dan berhenti berlangganan saat runtime, dan subject tidak perlu tahu class konkret mereka. Event listener di browser, EventEmitter Node.js, dan library reaktif seperti RxJS semuanya dibangun di atas gagasan ini.
Tujuan dan masalah yang diselesaikan
Tujuan: mendefinisikan ketergantungan satu-ke-banyak antarobjek sehingga saat satu objek berubah state, semua dependennya diberi tahu dan diperbarui secara otomatis. Observer adalah salah satu pola behavioral dalam buku Gang of Four (GoF), dan juga dikenal sebagai publish-subscribe atau pola listener.
Bayangkan keranjang belanja di toko web. Saat item ditambahkan, ikon keranjang di header harus menampilkan jumlah baru, sidebar harus menampilkan total baru, dan modul analytics harus mencatat event-nya. Jika keranjang memanggil semuanya secara langsung, modul keranjang bergantung pada header, sidebar, dan analytics. Menambah reaksi keempat berarti mengedit keranjang lagi.
Dengan Observer, keranjang hanya menyimpan daftar subscriber dan memanggil masing-masing saat berubah. Header, sidebar, dan analytics mendaftarkan diri sendiri. Keranjang tidak meng-import mereka, dan fitur baru bisa bereaksi terhadap perubahan keranjang tanpa menyentuh kode keranjang.
Struktur dan peserta
| Peran | Tanggung jawab |
|---|---|
| Subject (publisher) | Menyimpan daftar observer dan menyediakan method untuk mendaftarkan dan membatalkan langganan mereka; memberi tahu mereka saat ada perubahan |
| Observer (subscriber) | Interface dengan method update, sering hanya berupa fungsi callback |
| Concrete subject | Menyimpan state yang diminati dan memanggil notify saat berubah |
| Concrete observer | Bereaksi terhadap notifikasi, misalnya dengan menggambar ulang sebagian UI |
Ada dua gaya notifikasi. Pada model push, subject mengirim data yang berubah bersama notifikasi. Pada model pull, ia hanya mengatakan "ada yang berubah" dan observer meminta data yang dibutuhkan dari subject.
Implementasi TypeScript
Class generik Subject yang kecil menangani langganan. Observer adalah fungsi biasa, bentuk yang paling umum di TypeScript. subscribe mengembalikan fungsi untuk berhenti berlangganan, sehingga pemanggil tidak bisa lupa listener mana yang mereka daftarkan.
type Listener<T> = (value: T) => void;
class Subject<T> {
private listeners = new Set<Listener<T>>();
subscribe(listener: Listener<T>): () => void {
this.listeners.add(listener);
return () => this.listeners.delete(listener);
}
protected notify(value: T) {
for (const listener of [...this.listeners]) listener(value);
}
}
type CartEvent = { count: number; total: number };
class Cart extends Subject<CartEvent> {
private items: { name: string; price: number }[] = [];
add(name: string, price: number) {
this.items.push({ name, price });
const total = this.items.reduce((sum, item) => sum + item.price, 0);
this.notify({ count: this.items.length, total });
}
}
const cart = new Cart();
cart.subscribe(({ count }) => console.log("header: " + count + " items"));
const stopAnalytics = cart.subscribe(({ total }) => console.log("analytics: total " + total));
cart.add("Book", 12);
stopAnalytics(); // unsubscribe
cart.add("Pen", 3);
// header: 1 items
// analytics: total 12
// header: 2 itemsnotify mengiterasi salinan set ([...this.listeners]). Dengan begitu, listener yang berhenti berlangganan sendiri saat notifikasi tidak mengganggu loop.
Memakai EventTarget bawaan
Browser, Node.js, dan Deno menyediakan EventTarget, jadi Anda sering tidak perlu class subject sendiri:
const thermostat = new EventTarget();
thermostat.addEventListener("change", (e) => {
console.log("new temperature:", (e as CustomEvent<number>).detail);
});
thermostat.dispatchEvent(new CustomEvent("change", { detail: 21.5 }));
// new temperature: 21.5Node.js juga punya class EventEmitter di modul node:events, dengan method on, off, dan emit.
Kapan dipakai dan kapan tidak
- Pakai bila perubahan di satu objek harus memperbarui objek lain, dan Anda tidak tahu sebelumnya berapa banyak atau yang mana.
- Pakai untuk menjaga modul inti tetap independen dari kode UI, logging, atau analytics yang bereaksi terhadapnya.
- Pakai untuk event yang terjadi seiring waktu, seperti input pengguna, pesan, atau pembacaan sensor.
- Jangan dipakai bila hanya ada satu penerima tetap; pemanggilan method langsung atau parameter callback lebih mudah diikuti.
- Hati-hati dengan rantai observer yang memicu notifikasi lain. Hal itu dapat menyebabkan loop pembaruan dan membuat sulit melihat mengapa sesuatu berubah.
- Jangan bergantung pada urutan pemanggilan observer kecuali subject mendokumentasikannya.
Penggunaan di dunia nyata
- DOM:
element.addEventListener("click", handler)mendaftarkan observer ke sebuah elemen. - Node.js:
EventEmitteradalah dasar stream, server HTTP, dan banyak objek inti lainnya. - API observer di browser seperti
MutationObserver,IntersectionObserver, danResizeObservermemberi tahu callback tentang perubahan pada halaman. - RxJS memodelkan aliran nilai sebagai observable yang dilanggani observer.
- Java:
java.util.Observablesudah deprecated sejak Java 9; sebagai gantinya dipakai interfacejava.util.concurrent.Flow(publisher dan subscriber) serta interface event listener.
Pola terkait
| Pola | Perbedaannya dengan Observer |
|---|---|
| Mediator | Mediator mengenal objek yang berpartisipasi dan mengoordinasikan mereka; subject hanya menyiarkan dan tidak tahu apa yang dilakukan observer. |
| Publish-subscribe dengan broker | Publisher dan subscriber sama sekali tidak saling mengenal dan berkomunikasi lewat message broker atau event bus, sering antarproses. |
| Command | Sebuah notifikasi dapat membawa objek command; Command berkaitan dengan satu permintaan, Observer dengan notifikasi satu-ke-banyak. |
| Iterator | Iterator berbasis pull (konsumen meminta nilai berikutnya); Observer berbasis push (produsen mengirim nilai saat terjadi). |
FAQ
Apa perbedaan antara Observer dan publish-subscribe?
Pada pola Observer klasik, observer mendaftar langsung ke subject. Pada publish-subscribe, broker atau kanal event berada di antara keduanya, sehingga publisher dan subscriber tidak saling mengenal. Banyak orang memakai kedua nama itu secara longgar untuk gagasan yang sama.
Bagaimana menghindari memory leak dengan pola Observer?
Berhenti berlangganan observer saat tidak lagi dibutuhkan. Mengembalikan fungsi unsubscribe dari subscribe, atau memakai AbortSignal dengan addEventListener, membuat cleanup menjadi mudah.
Apakah observer dipanggil secara sinkron?
Pada contoh di atas serta di EventTarget dan EventEmitter, listener berjalan sinkron sesuai urutan penambahannya. Library lain mungkin mengirim notifikasi secara asinkron, jadi periksa dokumentasinya.
Apakah Observer sama dengan reactive programming?
Reactive programming dibangun di atas Observer. Library seperti RxJS menambahkan operator untuk memfilter, menggabungkan, dan mentransformasi aliran notifikasi, serta aturan untuk error dan penyelesaian.