30. Merancang class kecil

Kamu sudah bisa membangun class, membuat instance, dan melakukan inheritance. Bab terakhir Bagian 6 ini bukan tentang sintaks baru — ini tentang selera: merancang class yang nyaman dan aman digunakan. Ide besarnya, enkapsulasi, lebih sederhana dari kedengarannya.

Enkapsulasi: biarkan method menjaga data

Enkapsulasi berarti menjaga data object tetap valid dengan mengubahnya hanya melalui method-nya sendiri. Dunia luar meminta object untuk melakukan sesuatu; ia tidak pernah langsung mengacak field-fieldnya.

Berikut adalah BankAccount. Saldo tidak boleh pernah negatif, dan kamu tidak bisa mendepositkan jumlah negatif. Method-method yang menegakkan aturan tersebut:

class Account:
    def __init__(self, owner):
        self.owner = owner
        self.balance = 0

    def deposit(self, amount):
        if amount > 0:
            self.balance = self.balance + amount

    def withdraw(self, amount):
        if amount > 0 and amount <= self.balance:
            self.balance = self.balance - amount
            return True        # success
        return False           # not enough money, or a silly amount

    def get_balance(self):
        return self.balance

Cara menggunakannya:

acc = Account("Keiko")
acc.deposit(100)
acc.withdraw(30)
print(acc.get_balance())     # 70

print(acc.withdraw(1000))    # False  (refused: not enough)
print(acc.get_balance())     # 70     (unchanged)

Akun tidak pernah bisa mencapai state yang mustahil: setiap perubahan melewati method yang memeriksa terlebih dahulu. Jika kode luar bisa menulis acc.balance = -500 secara langsung, jaminan itu hilang. Jadi kesepakatannya adalah: bicara dengan object melalui method-nya.

Permukaan publik yang bersih

Anggap class memiliki dua sisi:

  • Bagian dalam — field dan logika bantu. Detail yang mungkin berubah.
  • Bagian luar — method yang dipanggil oleh kode lain. Sekumpulan aksi yang kecil dan jelas: deposit, withdraw, get_balance.

Class yang baik membuat bagian luar tetap kecil dan jelas. Pengguna Account milikmu hanya membutuhkan tiga kata kerja; mereka tidak perlu tahu bahwa saldo tersimpan di field bernama balance. Simpan dalam satuan sen besok, dan selama tiga method berperilaku sama, tidak ada yang rusak.

Beri nama method sebagai aksi

Method melakukan sesuatu, jadi beri nama dengan kata kerja: deposit, withdraw, move, attack, reset. Field menyimpan sesuatu, jadi beri nama dengan kata benda: balance, name, hp. acc.withdraw(30) harus terbaca seperti instruksi — memang itulah ia.

Buka exercises/30/01-account.py. Di sana ada class Account. Tambahkan method can_afford(amount) yang mengembalikan True jika saldo setidaknya amount, tanpa mengubahnya. Gunakan sebelum melakukan penarikan.

Tiga pertanyaan untuk setiap class

Saat merancang class, tanyakan:

  1. Apa yang diketahuinya? → field-fieldnya (akun tahu saldonya).
  2. Apa yang bisa dilakukannya? → method-methodnya (deposit, withdraw, cek).
  3. Apa yang harus selalu tetap benar? → aturan yang dijaga oleh method (saldo tidak pernah negatif).

Jawab ketiga pertanyaan itu dan class hampir menulis dirinya sendiri.

Pekerjaan Rumah

File pekerjaan rumah: exercises/30/homework/.

Soal 1 — Class Counter

Buka exercises/30/homework/01-counter.py. Rancang Counter yang dimulai dari 0 dengan increment(), get(), dan reset(). Nilai count hanya boleh berubah melalui method-method tersebut. Tunjukkan proses menghitung, membaca, dan mereset.

Soal 2 — Health yang dibatasi

Buka exercises/30/homework/02-health.py. Rancang class Health dengan nilai max. heal(n) menaikkan health tapi tidak pernah melebihi max; damage(n) menurunkannya tapi tidak pernah di bawah 0; get() membacanya. Aturan yang dijaga: health tetap antara 0 dan max.

Soal 3 — Saklar lampu

Buka exercises/30/homework/03-switch.py. Rancang Switch yang menyala atau mati. toggle() membaliknya, is_on() melaporkan statusnya. Mulai dalam keadaan mati, toggle beberapa kali, dan cetak status setelah masing-masing.

Tantangan — Stack dengan penjagaan

Buka exercises/30/homework/04-stack.py. Rancang Stack dengan push(v), pop(), dan size(). Aturan yang dijaga: pop() pada stack kosong tidak boleh crash — kembalikan None sebagai gantinya. Tunjukkan cara kerjanya, termasuk pop pada stack kosong.

Buntu atau sudah selesai? Buka halaman solusi pekerjaan rumah.