Inheritance menjawab relasi “adalah” (is-a): BukuReferensi adalah Buku. Namun sebagian besar relasi di dunia nyata berbentuk “memiliki” (has-a): perpustakaan memiliki koleksi buku, mobil memiliki mesin, pesanan memiliki daftar barang. Memaksakan relasi has-a lewat inheritance menghasilkan hierarki yang rapuh — mobil yang “mewarisi” mesin akan kesulitan ketika mesin harus bisa diganti tanpa membangun mobil baru. Composition adalah teknik untuk relasi has-a: sebuah object menyimpan object lain sebagai attribute, lalu bekerja sama dengannya. Chapter ini membahasnya lengkap dengan pola pendampingnya: delegation.

Relasi has-a: Mobil dan Mesin #

Bentuk paling sederhana dari composition: class menyimpan object lain di dalam attribute-nya sendiri:

python
class Mesin:
    def __init__(self, tenaga_kuda):
        self.tenaga_kuda = tenaga_kuda

    def hidupkan(self):
        return f"Mesin {self.tenaga_kuda} HP menyala"

class Mobil:
    def __init__(self, merek, tenaga_kuda):
        self.merek = merek
        self.mesin = Mesin(tenaga_kuda)

    def start(self):
        return f"{self.merek}: {self.mesin.hidupkan()}"

mobil = Mobil("Avanza", 100)

print(mobil.start())

Output yang Dihasilkan:

text
Avanza: Mesin 100 HP menyala

Mari kita telaah baris-baris kode di atas:

  1. Baris 8 (class Mobil:): Mobil tidak mewarisi Mesin. Tidak ada tanda kurung, tidak ada super() — hubungan keduanya bukan pewarisan.
  2. Baris 11 (self.mesin = Mesin(tenaga_kuda)): constructor Mobil membangun object Mesin dan menyimpannya pada attribute mesin. Mobil memiliki satu mesin.
  3. Baris 14 (return f"{self.merek}: {self.mesin.hidupkan()}"): method start() memakai object yang dimilikinya — notasi self.mesin.hidupkan() adalah wujud langsung relasi has-a.

Desain ini menjaga tiap class tetap kecil: Mesin hanya tahu soal mesin, Mobil hanya tahu cara mengoordinasikan komponen-komponennya.

Delegation: Meneruskan Pekerjaan #

Melangkah lebih jauh, Mobil bisa membebankan seluruh pekerjaan kepada komponen — pola ini disebut delegation. Method publik Mobil hanya menjadi pintu yang meneruskan panggilan ke object di dalamnya:

python
class Mesin:
    def __init__(self, tenaga_kuda):
        self.tenaga_kuda = tenaga_kuda

    def hidupkan(self):
        return f"Mesin {self.tenaga_kuda} HP menyala"

    def matikan(self):
        return "Mesin berhenti"

class Mobil:
    def __init__(self, merek, tenaga_kuda):
        self.merek = merek
        self.mesin = Mesin(tenaga_kuda)

    def start(self):
        return f"{self.merek}: {self.mesin.hidupkan()}"

    def stop(self):
        return f"{self.merek}: {self.mesin.matikan()}"

mobil = Mobil("Avanza", 100)

print(mobil.start())
print(mobil.stop())

Output yang Dihasilkan:

text
Avanza: Mesin 100 HP menyala
Avanza: Mesin berhenti

Pemanggil hanya berurusan dengan mobil.start() dan mobil.stop(); detail “bagaimana mesin menyala” sepenuhnya urusan class Mesin. Jika kelak mesin listrik masuk ke program — dengan method hidupkan() yang berbeda — Mobil tidak perlu ditulis ulang; cukup ganti object yang tersimpan di attribute mesin. Inilah fleksibilitas yang tidak dimiliki inheritance: komponen bisa dipertukarkan tanpa menyentuh hierarki class.

Komposisi di Domain Perpustakaan #

Kembali ke domain utama kita — Perpustakaan dan Buku adalah contoh komposisi yang utuh:

python
class Buku:
    def __init__(self, judul, penulis):
        self.judul = judul
        self.penulis = penulis

    def deskripsi(self):
        return f"{self.judul} ({self.penulis})"

class Perpustakaan:
    def __init__(self, nama):
        self.nama = nama
        self.koleksi = []

    def tambah_buku(self, buku):
        self.koleksi.append(buku)

    def daftar_koleksi(self):
        return [buku.deskripsi() for buku in self.koleksi]

perpus = Perpustakaan("Perpustakaan Kota")

perpus.tambah_buku(Buku("Laskar Pelangi", "Andrea Hirata"))
perpus.tambah_buku(Buku("Bumi Manusia", "Pramoedya A. Toer"))

print(perpus.nama)
print(perpus.daftar_koleksi())

Output yang Dihasilkan:

text
Perpustakaan Kota
['Laskar Pelangi (Andrea Hirata)', 'Bumi Manusia (Pramoedya A. Toer)']

Perpustakaan memiliki list yang berisi object-object Buku, dan setiap Buku tetap utuh dengan method serta attribute-nya sendiri — method daftar_koleksi() cukup memanggil buku.deskripsi() untuk tiap anggota. (Detail kecil: buku bisa ada tanpa perpustakaan mana pun, sehingga sebagian literatur menyebut relasi longgar seperti ini aggregation; prinsip teknisnya sama — object berisi object.)

Kapan Komposisi Lebih Baik daripada Inheritance #

Pertanyaan desain antara keduanya bisa diringkas dalam tabel:

PertanyaanJawabanPilihan yang tepat
Anak adalah versi khusus dari induk? (is-a)yainheritance
Class hanya menyimpan dan memakai object lain? (has-a)yacomposition
Komponen perlu bisa diganti saat program berjalan?yacomposition
Perilaku diambil dari banyak sumber berbeda?yacomposition / mixin

Ada satu pola yang menggabungkan keduanya dan sangat umum di ekosistem Python: class kecil yang sengaja hidup sebagai “bumbu” untuk dilewarkan ke class lain. Pola mixin itu — beserta izin Python mewarisi dari lebih dari satu class sekaligus — menjadi pokok bahasan Chapter Multiple Inheritance.