Langsung ke konten
KamusNgoding
Pemula Behavioral 5 menit baca

Apa itu Chain of Responsibility? Penjelasan Lengkap untuk Pemula

#design pattern #behavioral #chain of responsibility #pemula

Apa itu Chain of Responsibility? Penjelasan Lengkap untuk Pemula

Pendahuluan

Pernahkah kamu menghubungi layanan pelanggan sebuah aplikasi, lalu dipindahkan dari satu petugas ke petugas lain sampai masalahmu benar-benar terselesaikan? Itulah gambaran sederhana dari Chain of Responsibility — sebuah pola desain (design pattern) yang mengatur bagaimana sebuah permintaan diproses oleh serangkaian “penangan” secara berantai.

Jika kamu ingin membangun aplikasi seperti Gojek atau Tokopedia yang memiliki banyak lapisan validasi — mulai dari cek autentikasi, otorisasi peran, hingga validasi data — maka Chain of Responsibility adalah salah satu pola yang sangat berguna untuk dipahami.

Di artikel ini, kita akan belajar konsep Chain of Responsibility dari nol, lengkap dengan contoh kode yang bisa langsung kamu jalankan.


Apa itu Chain of Responsibility?

Chain of Responsibility (rantai tanggung jawab) adalah pola desain perilaku (behavioral design pattern) yang memungkinkan kamu meneruskan sebuah permintaan (request) melewati serangkaian handler (penangan). Setiap handler memutuskan apakah akan memproses permintaan itu atau meneruskannya ke handler berikutnya dalam rantai.

Pola ini termasuk dalam kelompok Behavioral Design Pattern, seperti halnya Apa itu Adapter Pattern? Penjelasan Lengkap untuk Pemula yang mengatur cara objek berinteraksi satu sama lain.

Analogi Kehidupan Nyata

Bayangkan kamu mengajukan permohonan cuti di kantor:

  1. Kamu → mengajukan permohonan ke atasan langsung
  2. Atasan langsung → jika cuti ≤ 3 hari, dia bisa langsung setujui. Jika lebih, diteruskan ke manajer.
  3. Manajer → jika cuti ≤ 7 hari, manajer setujui. Jika lebih, diteruskan ke HR.
  4. HR → menangani cuti lebih dari 7 hari.

Setiap pihak punya “batas kewenangan”. Jika permintaannya di luar batas mereka, mereka lempar ke orang berikutnya. Inilah inti dari Chain of Responsibility.

Kapan Menggunakannya?

Gunakan pola ini ketika:

  • Lebih dari satu objek bisa menangani sebuah permintaan, dan handler-nya tidak diketahui sebelumnya.
  • Kamu ingin meneruskan permintaan ke beberapa handler tanpa membuat pengirim tahu siapa yang akhirnya menanganinya.
  • Kamu ingin menambah atau mengubah urutan handler tanpa memodifikasi kode pengirim.

Komponen Utama dalam Chain of Responsibility

Ada tiga komponen utama dalam pola ini:

KomponenPeran
Handler (Interface/Abstract)Mendefinisikan antarmuka untuk menangani permintaan dan menyimpan referensi ke handler berikutnya
Concrete HandlerImplementasi nyata yang memutuskan apakah memproses permintaan atau meneruskannya
ClientPihak yang membuat dan mengirimkan permintaan ke rantai
Client → Handler A → Handler B → Handler C → (selesai / tidak ada yang menangani)

Handler (Abstract)

Handler adalah kelas abstrak atau interface yang menjadi “kontrak” bagi semua handler nyata. Ia menyediakan dua hal: method set_next() untuk menghubungkan handler berikutnya, dan method abstrak handle() yang wajib diimplementasikan oleh setiap turunannya.

from abc import ABC, abstractmethod

class Handler(ABC):
    def __init__(self):
        self._next_handler = None

    def set_next(self, handler):
        self._next_handler = handler
        return handler  # memungkinkan chaining: a.set_next(b).set_next(c)

    @abstractmethod
    def handle(self, request):
        pass

Concrete Handler

Concrete Handler adalah implementasi nyata dari Handler. Di sinilah logika bisnis sebenarnya berada. Setiap Concrete Handler hanya bertanggung jawab atas satu jenis pengecekan. Jika pengecekan berhasil, ia meneruskan permintaan ke _next_handler. Jika gagal, ia menghentikan rantai di sana.

Contoh dua Concrete Handler untuk validasi login:

# Concrete Handler 1: Cek username
class UsernameHandler(Handler):
    def handle(self, request):
        if not request.get("username"):
            print("❌ Gagal: Username tidak boleh kosong.")
            return False
        print("✅ Username valid, lanjut ke pengecekan berikutnya...")
        if self._next_handler:
            return self._next_handler.handle(request)
        return True

# Concrete Handler 2: Cek panjang password
class PasswordHandler(Handler):
    def handle(self, request):
        password = request.get("password", "")
        if len(password) < 8:
            print("❌ Gagal: Password minimal 8 karakter.")
            return False
        print("✅ Password valid, lanjut ke pengecekan berikutnya...")
        if self._next_handler:
            return self._next_handler.handle(request)
        return True

Client

Client adalah pihak yang membangun rantai handler dan mengirimkan permintaan ke awal rantai. Client tidak perlu tahu siapa yang akhirnya menangani permintaan — ia cukup mengirim ke handler pertama dan membiarkan rantai bekerja sendiri.

# Client: membangun rantai dan mengirim permintaan
def process_login(username, password, is_active):
    username_handler = UsernameHandler()
    password_handler = PasswordHandler()
    status_handler = AccountStatusHandler()

    # Sambungkan rantai
    username_handler.set_next(password_handler).set_next(status_handler)

    request = {"username": username, "password": password, "is_active": is_active}
    return username_handler.handle(request)

Implementasi Lengkap Chain of Responsibility

Mari gabungkan semua komponen menjadi contoh sistem validasi login yang lengkap dan bisa langsung dijalankan.

Contoh dalam Python

from abc import ABC, abstractmethod

# =====================
# HANDLER (Abstract)
# =====================
class Handler(ABC):
    def __init__(self):
        self._next_handler = None

    def set_next(self, handler):
        self._next_handler = handler
        return handler

    @abstractmethod
    def handle(self, request):
        pass


# =====================
# CONCRETE HANDLERS
# =====================
class UsernameHandler(Handler):
    def handle(self, request):
        if not request.get("username"):
            print("❌ Gagal: Username tidak boleh kosong.")
            return False
        print("✅ Username valid, lanjut ke pengecekan berikutnya...")
        if self._next_handler:
            return self._next_handler.handle(request)
        return True


class PasswordHandler(Handler):
    def handle(self, request):
        password = request.get("password", "")
        if len(password) < 8:
            print("❌ Gagal: Password minimal 8 karakter.")
            return False
        print("✅ Password valid, lanjut ke pengecekan berikutnya...")
        if self._next_handler:
            return self._next_handler.handle(request)
        return True


class AccountStatusHandler(Handler):
    def handle(self, request):
        if request.get("is_active") is False:
            print("❌ Gagal: Akun tidak aktif atau sudah diblokir.")
            return False
        print("✅ Akun aktif. Login berhasil!")
        return True


# =====================
# CLIENT
# =====================
def process_login(username, password, is_active):
    # Buat handler
    username_handler = UsernameHandler()
    password_handler = PasswordHandler()
    status_handler = AccountStatusHandler()

    # Sambungkan rantai
    username_handler.set_next(password_handler).set_next(status_handler)

    request = {
        "username": username,
        "password": password,
        "is_active": is_active
    }

    print(f"\n--- Memproses login untuk: '{username}' ---")
    return username_handler.handle(request)


# Uji coba
process_login("budi", "rahasia123", True)    # Semua lolos
process_login("", "rahasia123", True)         # Gagal di handler pertama
process_login("ani", "1234", True)            # Gagal di handler kedua
process_login("tono", "password99", False)   # Gagal di handler ketiga

Output:

--- Memproses login untuk: 'budi' ---
✅ Username valid, lanjut ke pengecekan berikutnya...
✅ Password valid, lanjut ke pengecekan berikutnya...
✅ Akun aktif. Login berhasil!

--- Memproses login untuk: '' ---
❌ Gagal: Username tidak boleh kosong.

--- Memproses login untuk: 'ani' ---
✅ Username valid, lanjut ke pengecekan berikutnya...
❌ Gagal: Password minimal 8 karakter.

--- Memproses login untuk: 'tono' ---
✅ Username valid, lanjut ke pengecekan berikutnya...
✅ Password valid, lanjut ke pengecekan berikutnya...
❌ Gagal: Akun tidak aktif atau sudah diblokir.

Perhatikan bagaimana setiap handler hanya fokus pada tugasnya sendiri. Jika pengecekan gagal, rantai berhenti di sana. Jika berhasil, permintaan diteruskan ke handler berikutnya.

Contoh dalam JavaScript

// =====================
// HANDLER (Base Class)
// =====================
class Handler {
  setNext(handler) {
    this.nextHandler = handler;
    return handler;
  }

  handle(request) {
    if (this.nextHandler) {
      return this.nextHandler.handle(request);
    }
    return null;
  }
}

// =====================
// CONCRETE HANDLERS
// =====================
class DebugHandler extends Handler {
  handle(request) {
    if (request.level === 'debug') {
      console.log(`[DEBUG] ${request.message}`);
      return;
    }
    super.handle(request);
  }
}

class InfoHandler extends Handler {
  handle(request) {
    if (request.level === 'info') {
      console.log(`[INFO] ${request.message}`);
      return;
    }
    super.handle(request);
  }
}

class ErrorHandler extends Handler {
  handle(request) {
    if (request.level === 'error') {
      console.error(`[ERROR] ${request.message}`);
      return;
    }
    super.handle(request);
  }
}

// =====================
// CLIENT
// =====================
const debug = new DebugHandler();
const info = new InfoHandler();
const error = new ErrorHandler();

// Sambungkan rantai
debug.setNext(info).setNext(error);

// Kirim berbagai pesan log
debug.handle({ level: 'info', message: 'Aplikasi berjalan normal' });
debug.handle({ level: 'debug', message: 'Nilai variabel x = 42' });
debug.handle({ level: 'error', message: 'Koneksi database gagal!' });

Output:

[INFO] Aplikasi berjalan normal
[DEBUG] Nilai variabel x = 42
[ERROR] Koneksi database gagal!

Contoh Kasus Nyata

1. Middleware di Framework Web

Jika kamu pernah menggunakan framework seperti Laravel atau Express.js, kamu sudah menggunakan Chain of Responsibility tanpa sadar! Sistem Authentication di Laravel: Login, Register, dan Middleware bekerja persis seperti rantai ini — setiap middleware memproses request HTTP secara berurutan sebelum mencapai controller.

Urutan middleware di Laravel biasanya:

Request → CheckMaintenance → ValidateCSRF → AuthMiddleware → RoleMiddleware → Controller

2. Sistem Persetujuan Bertingkat

Bayangkan kamu membangun fitur persetujuan pembelian untuk sebuah startup. Nilai pembelian menentukan siapa yang harus menyetujui:

from abc import ABC, abstractmethod

class ApprovalHandler(ABC):
    def __init__(self):
        self._next = None

    def set_next(self, handler):
        self._next = handler
        return handler

    @abstractmethod
    def approve(self, amount):
        pass


class TeamLeadApproval(ApprovalHandler):
    def approve(self, amount):
        if amount <= 500_000:
            print(f"✅ Team Lead menyetujui pembelian Rp{amount:,}")
        elif self._next:
            print(f"⏩ Jumlah Rp{amount:,} terlalu besar untuk Team Lead, eskalasi...")
            self._next.approve(amount)
        else:
            print(f"❌ Tidak ada handler yang bisa menyetujui Rp{amount:,}")


class ManagerApproval(ApprovalHandler):
    def approve(self, amount):
        if amount <= 5_000_000:
            print(f"✅ Manager menyetujui pembelian Rp{amount:,}")
        elif self._next:
            print(f"⏩ Jumlah Rp{amount:,} terlalu besar untuk Manager, eskalasi...")
            self._next.approve(amount)
        else:
            print(f"❌ Tidak ada handler yang bisa menyetujui Rp{amount:,}")


class DirectorApproval(ApprovalHandler):
    def approve(self, amount):
        print(f"✅ Direktur menyetujui pembelian Rp{amount:,}")


# Client: susun rantai
team_lead = TeamLeadApproval()
manager = ManagerApproval()
director = DirectorApproval()

team_lead.set_next(manager).set_next(director)

# Uji coba
print("--- Pengajuan Rp300.000 ---")
team_lead.approve(300_000)

print("\n--- Pengajuan Rp2.000.000 ---")
team_lead.approve(2_000_000)

print("\n--- Pengajuan Rp10.000.000 ---")
team_lead.approve(10_000_000)

Output:

--- Pengajuan Rp300.000 ---
✅ Team Lead menyetujui pembelian Rp300,000

--- Pengajuan Rp2.000.000 ---
⏩ Jumlah Rp2,000,000 terlalu besar untuk Team Lead, eskalasi...
✅ Manager menyetujui pembelian Rp2,000,000

--- Pengajuan Rp10.000.000 ---
⏩ Jumlah Rp10,000,000 terlalu besar untuk Team Lead, eskalasi...
⏩ Jumlah Rp10,000,000 terlalu besar untuk Manager, eskalasi...
✅ Direktur menyetujui pembelian Rp10,000,000

Pertanyaan yang Sering Diajukan

Apa perbedaan Chain of Responsibility dengan if-else biasa?

if-else bertumpuk mengikat semua logika dalam satu fungsi besar, sehingga sulit diperluas dan diuji secara terpisah. Chain of Responsibility memisahkan setiap kondisi ke dalam kelas handler sendiri. Ini membuat kode lebih bersih, mudah diuji unit, dan terbuka untuk penambahan handler baru tanpa mengubah kode yang sudah ada — sesuai prinsip Open/Closed Principle.

Apa yang terjadi jika tidak ada handler yang bisa menangani permintaan?

Tergantung implementasimu. Kamu bisa membiarkan permintaan lewat begitu saja (tidak diproses), atau membuat handler terakhir sebagai default handler yang menangkap semua permintaan yang lolos. Pilihan ini tergantung kebutuhan aplikasimu — yang penting pastikan perilaku ini terdokumentasi dengan jelas.

Bagaimana cara menambah handler baru tanpa merusak kode yang ada?

Cukup buat class baru yang extends Handler, lalu sisipkan ke dalam rantai di bagian client. Kamu tidak perlu mengubah handler yang sudah ada sama sekali. Inilah salah satu keunggulan terbesar dari pola ini — sangat mendukung prinsip Open/Closed dalam SOLID.

Apakah urutan handler penting?

Sangat penting. Handler dieksekusi berurutan dari yang pertama ke terakhir. Pastikan handler yang paling spesifik atau paling ketat berada di awal rantai agar permintaan yang tidak valid tersaring lebih awal, menghemat proses komputasi yang tidak perlu.

Apakah Chain of Responsibility sama dengan Pipeline Pattern?

Keduanya mirip, tapi berbeda tujuan. Chain of Responsibility fokus pada siapa yang bertanggung jawab menangani permintaan — setiap handler bisa memilih untuk memproses atau melempar ke berikutnya. Pipeline Pattern fokus pada transformasi data secara berurutan di mana setiap tahap pasti memproses data sebelum meneruskannya.


Kesimpulan

Chain of Responsibility adalah pola desain yang elegan untuk mengelola logika bertingkat tanpa membuat kode menjadi tumpukan if-else yang tidak terkendali. Dengan memisahkan setiap tanggung jawab ke dalam handler tersendiri, kode kamu menjadi lebih modular, mudah diuji, dan mudah diperluas.

Pola ini sangat umum ditemukan di dunia nyata: middleware framework web, sistem logging, pipeline validasi formulir, hingga sistem persetujuan berlapis. Setelah memahami konsep ini, kamu akan mulai melihat pola yang sama di banyak kode yang kamu tulis sehari-hari.

Selamat belajar dan terus berlatih! Jangan berhenti di sini — eksplorasi lebih banyak konsep menarik lainnya di KamusNgoding dan jadikan kode kamu semakin rapi dan profesional.

Artikel Terkait