Law of Demeter — Clean Code

Law of Demeter (LoD) — juga disebut "Principle of Least Knowledge" — adalah aturan desain: sebuah objek hanya boleh berbicara dengan teman dekatnya, bukan teman

Law of Demeter (LoD) — juga disebut "Principle of Least Knowledge" — adalah aturan desain: sebuah objek hanya boleh berbicara dengan teman dekatnya, bukan teman dari temannya.

Dirumuskan Ian Holland di Northeastern University (1987), dinamai sesuai proyek tempat prinsip ini ditemukan.

Aturan praktis: Sebuah method M di class C hanya boleh memanggil method dari:

  1. Diri sendiri (this)
  2. Parameter yang dioper ke M
  3. Objek yang dibuat di dalam M
  4. Field langsung milik C

Tidak boleh: method dari objek yang didapat dari salah satu di atas.

Contoh pelanggaran (train wreck):

// Rantai panjang — train wreck!
function getPaperBoyAmount(customer) {
  return customer.getWallet().getMoney().getAmount();
}

// Masalah:
// 1. Fungsi tahu struktur internal Customer (punya wallet)
// 2. Tahu struktur Wallet (punya money)
// 3. Tahu struktur Money (punya amount)
// Kalau Wallet diganti jadi BankAccount — 10 file harus diedit (Shotgun Surgery)

Perbaikan: minta, jangan ambil

// Biarkan Customer yang tahu internal-nya
class Customer {
  getAvailableAmount() {
    return this.wallet.getMoney().getAmount();
  }
}

function getPaperBoyAmount(customer) {
  return customer.getAvailableAmount();
}
// Sekarang fungsi cuma "bicara" dengan Customer — tidak peduli Wallet diganti apa

Ini juga sering disebut "Tell, Don't Ask":

// ASK: tarik data keluar, putuskan di pemanggil
if (user.getAccount().getStatus() === "active" && user.getAccount().getBalance() > 0) {
  user.getAccount().withdraw(100);
}

// TELL: suruh objek melakukan sesuatu
if (user.canWithdraw(100)) {
  user.withdraw(100);
}

Real-world: Eloquent chain — kapan OK, kapan tidak?

Di Laravel, pola ini sangat umum:

$user->posts()->where("published", true)->latest()->first();

Ini bukan pelanggaran LoD — karena posts(), where(), latest(), first() semua mengembalikan tipe yang sama (Query Builder). Mereka bukan objek berbeda, cuma fluent API.

Bandingkan dengan:

// INI pelanggaran LoD
$user->getProfile()->getAddress()->getCity()->getCountry();
// User → Profile → Address → City → Country — 4 tipe berbeda

Aturan kepala: hitung tipe, bukan titik

Rantai . yang menyalurkan fluent API pada tipe sama (Query Builder, Stream, Promise) umumnya aman:

users
  .filter(u => u.isActive)
  .map(u => u.email)
  .sort();
// Semua Array — aman

Rantai . yang melompat antar tipe berbeda = bau LoD:

order.getCustomer().getBillingAddress().getStreet();
// Order → Customer → Address → Street — train wreck

Kapan LoD tidak berlaku: Data Structures

Jika objek memang Data Transfer Object (DTO) atau struktur data murni — bukan objek dengan behavior — LoD lebih longgar. Kamu memang harus akses field-field di dalamnya.

// DTO murni — akses langsung OK
const response = await api.fetchUser(id);
const city = response.data.address.city; // OK — ini data, bukan behavior

// Objek dengan behavior — akses via method
class User {
  getCity() { return this.address.city; }
}
user.getCity(); // hormati LoD

Kriteria: apakah objek ini punya perilaku yang bisa dilanggar kontrasinya?

Hide Delegate: refactoring pattern untuk LoD

// Sebelum
class Person {
  getDepartment() { return this.department; }
}
class Department {
  getManager() { return this.manager; }
}
// Client code
const manager = person.getDepartment().getManager();

// Sesudah — sembunyikan delegasi
class Person {
  getManager() { return this.department.getManager(); }
}
// Client code
const manager = person.getManager();

Trade-off: Person kini tahu tentang manager. Tapi client code tidak perlu tahu soal Department sama sekali.

Batas wajar: jangan paranoid

Menerapkan LoD ekstrem bisa melahirkan Middle Man smell — class yang isinya cuma delegasi:

class OrderAdapter {
  getCustomerName() { return this.order.getCustomer().getName(); }
  getCustomerEmail() { return this.order.getCustomer().getEmail(); }
  getCustomerPhone() { return this.order.getCustomer().getPhone(); }
  // ... 20 method delegasi lagi
}

Ini lebih buruk dari pelanggaran LoD. Kriteria akhir: pilih yang lebih mudah diubah besok.

Rangkuman:

🎭 Analogi sehari-hari

Law of Demeter itu kayak aturan minta tolong. Misal kamu butuh kunci kantor. Cara hormat: minta sama temenmu Budi (person.getKey()). Cara gak hormat (LoD violation): minta Budi mention nama bos-nya, ke security HR-nya, ke kepala building, baru dapet kunci (person.getBoss().getHR().getSecurity().getKey()). Selain ribet, kamu sekarang TAU strukturnya — kalo struktur bos-HR-security berubah, kamu kena. Hormati LoD = JANGAN tau struktur internal teman, minta langsung. Plus awas: terlalu paranoid LoD = adapter class yang isinya 50% delegate method (Middle Man smell). Balance.

⚠️ Jebakan yang sering ditemui

Kapan LoD Berlaku Ketat vs Longgar

Ketat:

Longgar:

Hide Delegate Pattern

// SEBELUM — train wreck
const managerName = order.getCustomer().getDepartment().getManager().getName()

// SESUDAH — Hide Delegate (Order tau cara sampai ke manager name)
class Order {
  getManagerName() {
    return this.customer.getDepartment().getManager().getName()
  }
}
const managerName = order.getManagerName()
// Client code gak tau struktur Customer/Department/Manager

🎯 Ini smell LoD atau bukan?

  • a.b().c().d() antar class beda dengan behavior → smell ✓
  • Fluent API tipe sama (query.where().orderBy()) → aman, gak smell
  • Akses DTO field (response.data.user.name) → aman
  • Method chain di array/string (arr.filter().map().sort()) → aman
  • Adapter class dengan 30 delegate method → Middle Man smell (over-correction)
  • Optional chaining null safety (a?.b?.c) → BUKAN LoD issue, tapi nullable handling

Aturan: kalo train wreck-nya nyebrang antar tipe berbeda yang punya behavior, refactor. Sisanya, gunakan judgment.

TL;DR: Law of Demeter (LoD) = "Principle of Least Knowledge". Object cuma boleh ngomong sama teman dekat, bukan teman-dari-teman. Train wreck a.b().c().d() antar tipe beda = smell, fix pakai Hide Delegate. Tidak berlaku untuk DTO, fluent API tipe sama, method chain di array. Over-correct = Middle Man smell (class adapter yang isinya delegate semua). Balance: hindari train wreck tapi jangan paranoid bikin adapter berlebihan.

Yang akan kamu pelajari