Langsung ke konten
KamusNgoding
Menengah Behavioral 6 menit baca

Mediator vs Observer Pattern: Perbedaan dan Kapan Menggunakannya

#design pattern #behavioral #mediator #observer #arsitektur

Pendahuluan

Ketika aplikasi semakin besar, komunikasi antar objek bisa menjadi mimpi buruk. Bayangkan kamu sedang membangun fitur notifikasi untuk aplikasi e-commerce seperti Tokopedia — ada modul pesanan, modul inventori, modul email, dan modul push notification yang semuanya perlu “berbicara” satu sama lain. Jika setiap modul langsung terhubung ke modul lainnya, kode kamu akan menjadi spaghetti yang sulit dipelihara.

Di sinilah dua design pattern hadir sebagai solusi: Mediator dan Observer. Keduanya mengelola komunikasi antar objek, tetapi dengan pendekatan yang berbeda. Artikel ini akan menjelaskan keduanya secara mendalam, membandingkan perbedaannya, dan membantu kamu memutuskan mana yang tepat untuk kasusmu.


Memahami Mediator Pattern

Mediator Pattern adalah pola desain behavioral yang mendefinisikan objek pusat (mediator) sebagai perantara komunikasi antara berbagai objek (colleagues). Objek-objek ini tidak lagi berkomunikasi langsung satu sama lain — semua pesan melewati mediator.

Analogi sederhananya: bayangkan tower kontrol bandara. Pilot pesawat tidak berkomunikasi langsung satu sama lain. Semua komunikasi melewati tower kontrol yang mengatur lalu lintas penerbangan.

from abc import ABC, abstractmethod

# Interface Mediator
class ChatMediator(ABC):
    @abstractmethod
    def send_message(self, message: str, sender: "User") -> None:
        pass

    @abstractmethod
    def add_user(self, user: "User") -> None:
        pass


# Concrete Mediator
class ChatRoom(ChatMediator):
    def __init__(self):
        self._users: list["User"] = []

    def add_user(self, user: "User") -> None:
        self._users.append(user)

    def send_message(self, message: str, sender: "User") -> None:
        for user in self._users:
            if user != sender:
                user.receive(message, sender.name)


# Colleague
class User:
    def __init__(self, name: str, mediator: ChatMediator):
        self.name = name
        self._mediator = mediator
        mediator.add_user(self)

    def send(self, message: str) -> None:
        print(f"[{self.name}] mengirim: {message}")
        self._mediator.send_message(message, self)

    def receive(self, message: str, sender_name: str) -> None:
        print(f"[{self.name}] menerima dari {sender_name}: {message}")


# Penggunaan
room = ChatRoom()
budi = User("Budi", room)
sari = User("Sari", room)
andi = User("Andi", room)

budi.send("Halo semua!")
# Output:
# [Budi] mengirim: Halo semua!
# [Sari] menerima dari Budi: Halo semua!
# [Andi] menerima dari Budi: Halo semua!

Perhatikan bahwa Budi tidak tahu siapa saja anggota chat room. Semua dikelola oleh ChatRoom sebagai mediator.


Memahami Observer Pattern

Observer Pattern adalah pola di mana sebuah objek (subject atau publisher) memelihara daftar dependennya (observers atau subscribers) dan memberi tahu mereka secara otomatis saat ada perubahan state.

Analogi yang tepat: seperti berlangganan newsletter. Kamu (observer) mendaftar ke sebuah blog (subject). Setiap kali ada artikel baru, kamu otomatis mendapat notifikasi — tanpa perlu terus-menerus mengecek sendiri.

Jika kamu sudah familiar dengan Observer dari sudut pandang pengenalan dasar, artikel ini memperdalam perbandingannya dengan Mediator. Untuk review konsep Observer secara lebih dasar, kamu bisa membaca Apa itu Observer Pattern? Penjelasan Lengkap untuk Pemula.

from abc import ABC, abstractmethod
from typing import List

# Interface Observer
class StockObserver(ABC):
    @abstractmethod
    def update(self, stock_name: str, price: float) -> None:
        pass


# Interface Subject
class StockSubject(ABC):
    @abstractmethod
    def subscribe(self, observer: StockObserver) -> None:
        pass

    @abstractmethod
    def unsubscribe(self, observer: StockObserver) -> None:
        pass

    @abstractmethod
    def notify(self) -> None:
        pass


# Concrete Subject
class StockMarket(StockSubject):
    def __init__(self):
        self._observers: List[StockObserver] = []
        self._stocks: dict = {}

    def subscribe(self, observer: StockObserver) -> None:
        self._observers.append(observer)

    def unsubscribe(self, observer: StockObserver) -> None:
        self._observers.remove(observer)

    def set_price(self, stock_name: str, price: float) -> None:
        self._stocks[stock_name] = price
        self.notify()

    def notify(self) -> None:
        for observer in self._observers:
            for name, price in self._stocks.items():
                observer.update(name, price)


# Concrete Observer
class InvestorApp(StockObserver):
    def __init__(self, investor_name: str):
        self.name = investor_name

    def update(self, stock_name: str, price: float) -> None:
        print(f"[{self.name}] Notifikasi: {stock_name} = Rp {price:,.0f}")


class AlertSystem(StockObserver):
    def __init__(self, threshold: float):
        self.threshold = threshold

    def update(self, stock_name: str, price: float) -> None:
        if price > self.threshold:
            print(f"[ALERT] {stock_name} melampaui threshold! Harga: Rp {price:,.0f}")


# Penggunaan
market = StockMarket()
budi_app = InvestorApp("Budi")
alert = AlertSystem(threshold=50000)

market.subscribe(budi_app)
market.subscribe(alert)

market.set_price("BBCA", 45000)
market.set_price("TLKM", 55000)
# Output:
# [Budi] Notifikasi: BBCA = Rp 45.000
# [Budi] Notifikasi: TLKM = Rp 55.000
# [ALERT] TLKM melampaui threshold! Harga: Rp 55.000

Perbedaan Utama: Mediator vs Observer

Meski keduanya mengelola komunikasi antar objek, ada perbedaan fundamental yang harus dipahami:

AspekMediatorObserver
Arah komunikasiBanyak-ke-banyak melalui pusatSatu-ke-banyak (broadcast)
Kesadaran antar objekColleagues tidak tahu satu sama lainSubject tahu ada observers, tapi tidak spesifik siapa
Kontrol alurMediator bisa mengatur logika komunikasiSubject hanya memberi tahu, tidak mengontrol reaksi
KompleksitasMediator bisa menjadi God Object jika tidak hati-hatiLebih sederhana, tapi bisa susah di-debug jika observers banyak
Use case utamaKoordinasi kompleks antar komponenEvent broadcasting / reactive programming

Perbedaan inti dalam satu kalimat:

  • Mediator = “Semua komunikasi lewat saya, saya yang atur siapa bicara ke siapa”
  • Observer = “Saya punya perubahan, siapapun yang tertarik silakan bereaksi”

Kapan Menggunakan Mediator Pattern

Gunakan Mediator ketika:

  1. Banyak objek saling berinteraksi dan hubungannya kompleks — setiap objek tahu tentang terlalu banyak objek lain.
  2. Logika komunikasi perlu dikontrol atau difilter secara terpusat.
  3. Reuse komponen — kamu ingin menggunakan komponen yang sama di konteks berbeda tanpa mengubah komponen itu sendiri.

Contoh nyata: Form Wizard

// Mediator untuk Form Multi-Step
class FormWizardMediator {
  constructor() {
    this.components = {};
    this.currentStep = 1;
  }

  register(name, component) {
    this.components[name] = component;
    component.setMediator(this);
  }

  notify(sender, event) {
    if (event === 'step1_complete') {
      this.currentStep = 2;
      this.components['step2'].show();
      this.components['progressBar'].update(50);
      this.components['backButton'].enable();
    }

    if (event === 'step2_complete') {
      this.currentStep = 3;
      this.components['step3'].show();
      this.components['progressBar'].update(100);
      this.components['submitButton'].enable();
    }

    if (event === 'back_clicked') {
      if (this.currentStep > 1) {
        this.currentStep--;
        this.components[`step${this.currentStep}`].show();
        this.components['progressBar'].update((this.currentStep / 3) * 100);
      }
    }
  }
}

class StepComponent {
  constructor(name) {
    this.name = name;
    this.mediator = null;
  }

  setMediator(mediator) {
    this.mediator = mediator;
  }

  complete() {
    console.log(`${this.name} selesai diisi`);
    this.mediator.notify(this, `${this.name}_complete`);
  }

  show() {
    console.log(`Menampilkan ${this.name}`);
  }
}

// Jika kamu ingin membangun aplikasi onboarding seperti yang ada di Gojek
// atau Tokopedia, pola ini sangat berguna untuk multi-step form yang kompleks.
const mediator = new FormWizardMediator();
const step1 = new StepComponent('step1');
const step2 = new StepComponent('step2');
const step3 = { setMediator: () => {}, show: () => console.log('Menampilkan step3') };
const progressBar = { setMediator: () => {}, update: (v) => console.log(`Progress: ${v}%`) };
const backButton = { setMediator: () => {}, enable: () => console.log('Back button aktif') };
const submitButton = { setMediator: () => {}, enable: () => console.log('Submit button aktif') };

mediator.register('step1', step1);
mediator.register('step2', step2);
mediator.register('step3', step3);
mediator.register('progressBar', progressBar);
mediator.register('backButton', backButton);
mediator.register('submitButton', submitButton);

step1.complete();
// Output:
// step1 selesai diisi
// Menampilkan step2
// Progress: 50%
// Back button aktif

Kapan Menggunakan Observer Pattern

Gunakan Observer ketika:

  1. Perubahan satu objek perlu ditransmisikan ke banyak objek lain tanpa kamu tahu siapa saja mereka.
  2. Decoupling adalah prioritas — publisher tidak perlu tahu tentang subscribers.
  3. Event-driven architecture — misalnya, sistem notifikasi, real-time updates, atau reactive UI.

Jika kamu bekerja dengan React, Observer pattern ini sangat mirip dengan konsep state management dan event emitter. Untuk implementasi yang lebih mendalam di ekosistem React, kamu bisa baca React dengan TypeScript: Komponen yang Type-Safe sebagai fondasi sebelum menerapkan Observer di komponen React.

Contoh nyata: Sistem Notifikasi E-commerce

# Sistem notifikasi pesanan — mirip alur di platform e-commerce
class OrderEventEmitter:
    def __init__(self):
        self._listeners = {}

    def on(self, event: str, callback):
        if event not in self._listeners:
            self._listeners[event] = []
        self._listeners[event].append(callback)

    def emit(self, event: str, data: dict):
        if event in self._listeners:
            for callback in self._listeners[event]:
                callback(data)


# Inisialisasi event emitter
order_events = OrderEventEmitter()

# Observer 1: Kirim email konfirmasi
def send_email_confirmation(order_data):
    print(f"[Email] Pesanan #{order_data['id']} dikonfirmasi ke {order_data['email']}")

# Observer 2: Kurangi stok
def update_inventory(order_data):
    print(f"[Inventory] Mengurangi stok produk: {order_data['product']}")

# Observer 3: Kirim push notification
def send_push_notification(order_data):
    print(f"[Push] Pesananmu #{order_data['id']} sedang diproses!")

# Observer 4: Catat ke analytics
def track_analytics(order_data):
    print(f"[Analytics] Event 'order_placed' tercatat untuk produk: {order_data['product']}")

# Daftarkan semua observer ke event 'order_placed'
order_events.on('order_placed', send_email_confirmation)
order_events.on('order_placed', update_inventory)
order_events.on('order_placed', send_push_notification)
order_events.on('order_placed', track_analytics)

# Trigger event
order_events.emit('order_placed', {
    'id': 'ORD-2024-001',
    'email': '[email protected]',
    'product': 'Sepatu Lari Nike'
})

# Output:
# [Email] Pesanan #ORD-2024-001 dikonfirmasi ke [email protected]
# [Inventory] Mengurangi stok produk: Sepatu Lari Nike
# [Push] Pesananmu #ORD-2024-001 sedang diproses!
# [Analytics] Event 'order_placed' tercatat untuk produk: Sepatu Lari Nike

Contoh Kasus Nyata

Kasus 1: Aplikasi Chat (Gunakan Mediator)

Bayangkan ingin membangun fitur grup chat seperti di Slack atau WhatsApp Web. Ada banyak user yang bisa bergabung dan keluar kapan saja. Mediator cocok di sini karena:

  • Setiap user hanya berkomunikasi dengan Chat Room, bukan langsung ke user lain
  • Chat Room bisa menambahkan logika seperti filter kata, rate limiting, atau logging terpusat

Kasus 2: Dashboard Analitik Real-time (Gunakan Observer)

Jika ingin membangun dashboard seperti yang digunakan tim data di perusahaan fintech — di mana setiap update data transaksi otomatis memperbarui grafik, tabel, dan alert — Observer adalah pilihan tepat karena:

  • Data source tidak perlu tahu siapa yang mendengarkan
  • Widget baru bisa ditambahkan atau dihapus tanpa mengubah data source
  • Cocok untuk arsitektur event-driven

Kasus 3: Sistem Traffic Control Bandara (Mediator)

Seperti analogi di awal, ketika ada puluhan pesawat yang perlu berkoordinasi, mediator mencegah chaos komunikasi O(n²) menjadi O(n) — setiap pesawat hanya perlu berbicara ke satu entitas.


Pertanyaan yang Sering Diajukan

Apa perbedaan utama antara Mediator dan Observer?

Mediator mengontrol komunikasi banyak-ke-banyak melalui satu titik pusat, dan mediator bisa memiliki logika untuk menentukan siapa menerima pesan apa. Observer adalah mekanisme broadcast satu-ke-banyak di mana subject hanya memberi tahu semua subscriber tanpa logika pengiriman khusus.

Bisakah Mediator dan Observer digunakan bersamaan?

Ya, dan ini cukup umum dalam aplikasi besar. Misalnya, kamu bisa menggunakan Observer untuk event system antar modul, dan Mediator di dalam satu modul untuk mengkoordinasikan komponen-komponen UI yang kompleks. Keduanya saling melengkapi, bukan saling eksklusif.

Mengapa Mediator bisa menjadi antipattern jika tidak hati-hati?

Mediator berisiko menjadi God Object — satu kelas yang tahu terlalu banyak dan melakukan terlalu banyak hal. Jika semua logika bisnis dimasukkan ke mediator, kode menjadi sulit di-test dan dipelihara. Solusinya: pisahkan mediator berdasarkan domain atau fitur, bukan satu mediator untuk seluruh aplikasi.

Bagaimana cara memilih antara Mediator dan Observer untuk event system?

Gunakan Observer jika publisher tidak perlu tahu siapa subscribernya dan reaksi subscriber tidak perlu dikoordinasikan. Gunakan Mediator jika ada kebutuhan untuk mengontrol urutan, memfilter pesan, atau mengkoordinasikan respons dari beberapa komponen secara bersamaan.

Apakah Observer Pattern sama dengan Event Emitter di Node.js?

Ya, EventEmitter di Node.js adalah implementasi Observer Pattern. Konsepnya identik: kamu mendaftarkan listener (observer) untuk event tertentu, dan emitter (subject) membroadcast event beserta datanya ke semua listener yang terdaftar.


Kesimpulan

Mediator dan Observer adalah dua senjata ampuh untuk mengelola komunikasi antar objek dalam sistem yang kompleks. Mediator unggul saat kamu butuh koordinasi terpusat dan kontrol penuh atas alur komunikasi — cocok untuk form wizard, sistem chat, atau UI dengan banyak komponen yang saling bergantung. Observer bersinar saat kamu butuh decoupling maksimal dan broadcasting event — cocok untuk notifikasi, real-time dashboard, atau arsitektur event-driven.

Kunci memilihnya sederhana: jika objek-objek kamu perlu berkoordinasi, pilih Mediator. Jika mereka hanya perlu diberitahu, pilih Observer.

Selamat bereksperimen dengan kedua pattern ini di project kamu berikutnya! Jangan ragu untuk mengeksplorasi artikel-artikel lain di KamusNgoding dan terus asah kemampuan desain softwaremu — setiap pattern yang kamu kuasai adalah satu langkah lebih dekat menjadi developer yang handal.

Artikel Terkait