30. Merancang class yang baik
Kamu sudah bisa membuat class, membuat instance, dan menggunakan pewarisan. Bab terakhir dari Bagian 6 ini bukan tentang sintaks baru — ini tentang selera desain: merancang class yang nyaman dan aman digunakan. Ide besarnya, enkapsulasi, lebih sederhana dari kedengarannya.
Enkapsulasi: biarkan method menjaga data
Enkapsulasi artinya menjaga data objek tetap valid dengan mengubahnya hanya melalui method-method miliknya sendiri. Kode dari luar cukup meminta objek untuk melakukan sesuatu; ia tidak perlu langsung mengacak-acak field-nya.
Berikut contoh class BankAccount. Saldo tidak boleh
pernah negatif, dan kamu tidak bisa menyetor jumlah negatif.
Method-lah yang menegakkan aturan-aturan tersebut:
local Account = {}
Account.__index = Account
function Account.new(owner)
return setmetatable({ owner = owner, balance = 0 }, Account)
end
function Account:deposit(amount)
if amount > 0 then
self.balance = self.balance + amount
end
end
function Account:withdraw(amount)
if amount > 0 and amount <= self.balance then
self.balance = self.balance - amount
return true -- success
end
return false -- not enough money, or a silly amount
end
function Account:getBalance()
return self.balance
endCara menggunakannya:
local acc = Account.new("Keiko")
acc:deposit(100)
acc:withdraw(30)
print(acc:getBalance()) -- 70
print(acc:withdraw(1000)) -- false (ditolak: saldo tidak cukup)
print(acc:getBalance()) -- 70 (tidak berubah)Akun ini tidak bisa mencapai kondisi yang tidak masuk akal: setiap
perubahan melewati method yang memeriksa dulu. Kalau kode dari luar bisa
menulis acc.balance = -500 langsung, jaminan itu hilang.
Jadi kesepakatannya: bicara dengan objek melalui
method-nya.
Antarmuka publik yang bersih
Bayangkan sebuah class punya dua sisi:
- Sisi dalam — field dan logika bantu internalnya. Detail yang mungkin berubah.
- Sisi luar — method yang dipanggil oleh kode lain.
Sekumpulan aksi yang kecil dan jelas:
deposit,withdraw,getBalance.
Class yang baik menjaga sisi luar tetap kecil dan jelas. Pengguna
class Account-mu hanya butuh tiga kata kerja; mereka tidak
perlu tahu bahwa saldo disimpan dalam field bernama
balance. Simpan dalam satuan sen besok, dan selama ketiga
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 — karena memang begitu.
Buka exercises/30/01-account.lua. Di sana ada class
BankAccount. Tambahkan method
:canAfford(amount) yang mengembalikan true
jika saldo minimal sebesar amount, tanpa mengubahnya.
Gunakan method itu sebelum melakukan penarikan.
Tiga pertanyaan untuk setiap class
Saat merancang sebuah class, tanyakan:
- Apa yang ia tahu? → field-fieldnya (sebuah akun tahu saldo miliknya).
- Apa yang bisa ia lakukan? → method-methodnya (setor, tarik, cek).
- Apa yang harus selalu benar? → aturan yang dijaga oleh method (saldo tidak pernah negatif).
Jawab ketiga pertanyaan itu dan class-nya hampir menulis dirinya sendiri.
PR
File PR: exercises/30/homework/.
Soal 1 — Class Counter
Buka exercises/30/homework/01-counter.lua. Rancang
sebuah Counter yang dimulai dari 0 dengan
method :increment(), :get(), dan
:reset(). Nilai hitungan hanya boleh berubah melalui
method-method tersebut. Tunjukkan cara menghitung, membaca, dan
mereset.
Soal 2 — Health yang dibatasi
Buka exercises/30/homework/02-health.lua. 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 harus
dijaga: health selalu berada antara 0 dan
max.
Soal 3 — Saklar lampu
Buka exercises/30/homework/03-switch.lua. Rancang sebuah
Switch yang bisa menyala atau mati. :toggle()
membalik kondisinya, :isOn() melaporkan kondisinya. Mulai
dalam kondisi mati, toggle beberapa kali, dan cetak kondisinya setelah
setiap toggle.
Tantangan — Stack dengan penjaga
Buka exercises/30/homework/04-stack.lua. Rancang sebuah
Stack dengan method :push(v),
:pop(), dan :size(). Aturan yang harus dijaga:
:pop() pada stack kosong tidak boleh crash — kembalikan
nil sebagai gantinya. Tunjukkan cara kerjanya, termasuk
pop pada stack kosong.
Bingung atau sudah selesai? Buka halaman solusi PR.