Pola OOP

Pembangunan objek dengan sintaks titik dua, self, dan template kelas Lua.

Daftar isi

Lua tidak memiliki kata kunci class, tetapi pemrograman berorientasi objek (object-oriented programming, OOP) tetap dapat dibangun dari table dan metatable. Topik ini membahas sintaks titik dua beserta self, yakni objek penerima yang disisipkan secara otomatis, dilanjutkan dengan templat kelas klasik beserta alasan baris Kelas.__index = Kelas diperlukan. Satu kelas utuh kemudian disimpan sebagai berkas aset dan dimuat dari blok kode lain untuk menunjukkan penggunaannya secara utuh.

Sintaks titik dua dan self#

Definisi dan pemanggilan metode menggunakan tanda titik dua yang menyisipkan argumen tersembunyi bernama self. Bentuk pemanggilan t:metode(a) setara dengan t.metode(t, a), sehingga objek penerima otomatis menjadi argumen pertama. Pada sisi definisi, function T:metode(a) setara dengan function T.metode(self, a). Contoh berikut mendefinisikan metode dengan bentuk eksplisit lalu memanggilnya dalam kedua bentuk.

lua
local penghitung = {n = 0}

function penghitung.naik(self, langkah)
  self.n = self.n + (langkah or 1)
  return self.n
end

print(penghitung:naik())
print(penghitung.naik(penghitung, 5))
print(penghitung:naik(2))
text Keluaran
1
6
8

Ketiga baris keluaran berasal dari satu metode yang sama. Baris pertama menyisipkan penghitung sebagai self lalu membiarkan langkah bernilai nil sehingga nilai bertambah 1, baris kedua memberikan self dan langkah secara eksplisit, dan baris ketiga kembali ke bentuk titik dua. Kesetaraan ini penting dipahami karena seluruh pola OOP pada bagian berikut hanya memanfaatkan mekanisme yang sama. Nilai langkah yang boleh absen juga menunjukkan alasan bentuk titik dua praktis, karena self tersedia tanpa risiko terlupa ditulis pada setiap pemanggilan.

Templat kelas#

Templat kelas klasik memisahkan data dan perilaku ke dalam dua table. Data berupa field tersimpan pada objek, sedangkan perilaku berupa metode berkumpul pada table kelas. Baris kunci templatnya adalah Kelas.__index = Kelas, yang menjadikan table kelas sebagai tujuan pencarian bagi kunci yang tidak ada pada objek. Tanpa baris itu, metode tidak akan pernah ditemukan. Contoh berikut menerapkan templat pada kelas Catatan untuk daftar tugas pribadi.

lua
local Catatan = {}
Catatan.__index = Catatan

function Catatan.baru(judul)
  local objek = setmetatable({}, Catatan)
  objek.judul = judul
  objek.selesai = false
  return objek
end

function Catatan:tandaiSelesai()
  self.selesai = true
end

function Catatan:uraikan()
  local status = "belum selesai"
  if self.selesai then
    status = "selesai"
  end
  return self.judul .. " (" .. status .. ")"
end

local tugasA = Catatan.baru("Kembalikan buku perpustakaan")
local tugasB = Catatan.baru("Isi jadwal piket")
tugasB:tandaiSelesai()

print(tugasA:uraikan())
print(tugasB:uraikan())
text Keluaran
Kembalikan buku perpustakaan (belum selesai)
Isi jadwal piket (selesai)

Fungsi baru membuat table kosong dengan setmetatable({}, Catatan) sehingga setiap kunci yang absen pada objek dicari ke Catatan, tempat metode berkumpul. Field judul dan selesai tersimpan langsung pada masing-masing objek, sedangkan tandaiSelesai dan uraikan hanya ada satu salinan di table kelas dan digunakan bersama oleh semua objek. Metode yang ditulis dengan bentuk titik dua menerima self tanpa perlu dideklarasikan ulang.

Data pada objek, perilaku di table kelas#

Pemisahan data dan perilaku terlihat jelas ketika dua objek dibandingkan. Setiap objek membawa field miliknya sendiri, sedangkan metode hanya tersimpan satu kali di table kelas dan digunakan bersama oleh semua objek. Contoh berikut membuat dua objek bel pemanggil, membunyikan salah satunya, lalu memeriksa lokasi field dan metodenya.

lua
local Dering = {}
Dering.__index = Dering

function Dering.baru(nada)
  local objek = setmetatable({nada = nada, berbunyi = false}, Dering)
  return objek
end

function Dering:bunyikan()
  self.berbunyi = true
end

local a = Dering.baru("ceng-ceng")
local b = Dering.baru("dung-dung")

a:bunyikan()

print(a.nada, a.berbunyi)
print(b.nada, b.berbunyi)
print(rawget(a, "bunyikan"))
print(getmetatable(a) == getmetatable(b))
text Keluaran
ceng-ceng	true
dung-dung	false
nil
true

Dua baris pertama menunjukkan field nada dan berbunyi tersimpan pada masing-masing objek, sehingga membunyikan a tidak memengaruhi b. Baris ketiga menggunakan rawget, yaitu pengaksesan langsung tanpa metatable, untuk membuktikan bahwa metode bunyikan tidak pernah tersimpan pada objek. Baris keempat menegaskan bahwa kedua objek berbagi metatable yang sama. Konsekuensinya, satu metode baru cukup didefinisikan sekali di table kelas untuk memperkaya seluruh objek yang sudah ada.

Kelas Kalkulator sebagai berkas aset#

Kelas berikut disimpan sebagai berkas kalkulator.lua di samping topik ini. Kelasnya menyimpan satu nilai hasil dan menyediakan tiga operasi yang mengembalikan self agar pemanggilan dapat dirantai. Berkas ditutup dengan return Kalkulator sehingga skrip lain dapat memuatnya lewat dofile, sedangkan menjalankannya langsung tidak mencetak apa pun karena berkasnya hanya mendefinisikan kelas.

lua kalkulator.lua
-- Kelas Kalkulator: menyimpan satu nilai hasil dan mengoperasikannya
local Kalkulator = {}
Kalkulator.__index = Kalkulator

function Kalkulator.baru(nilai_awal)
  local objek = setmetatable({}, Kalkulator)
  objek.hasil = nilai_awal or 0
  return objek
end

function Kalkulator:tambah(x)
  self.hasil = self.hasil + x
  return self
end

function Kalkulator:kurang(x)
  self.hasil = self.hasil - x
  return self
end

function Kalkulator:kali(x)
  self.hasil = self.hasil * x
  return self
end

return Kalkulator

Blok berikut memuat kelas dari berkasnya, lalu menggunakannya dalam bentuk titik dua dan bentuk titik dengan self eksplisit. Pemuatan sederhana menggunakan dofile yang menjalankan berkas dan mengembalikan nilainya, sedangkan require menambahkan pencarian path dan cache modul untuk kebutuhan yang lebih besar.

lua
local Kalkulator = dofile("kalkulator.lua")

local k = Kalkulator.baru(10)
k:tambah(5):kali(2)
print(k.hasil)

local m = Kalkulator.baru(20)
Kalkulator.kurang(m, 4)
print(m.hasil)
text Keluaran
30
16

Baris pertama keluaran berasal dari pemanggilan berantai k:tambah(5):kali(2) yang dimungkinkan karena tambah dan kali mengembalikan self, sehingga hasilnya 30 tanpa variabel perantara. Baris kedua menggunakan bentuk titik dengan self eksplisit pada Kalkulator.kurang(m, 4), yang setara dengan m:kurang(4). Hasilnya 16 menegaskan bahwa kedua bentuk menyentuh metode yang sama di table kelas.

Ringkasan#

  • Pemanggilan t:metode(a) setara dengan t.metode(t, a) karena titik dua menyisipkan self sebagai argumen pertama.
  • Templat kelas menaruh metode pada table kelas dan mengarahkan pencarian metode lewat baris Kelas.__index = Kelas.
  • Objek menyimpan field sedangkan metode hanya ada satu salinan di table kelas dan digunakan bersama oleh semua objek.
  • Metode yang mengembalikan self mendukung pemanggilan berantai, dan bentuk titik dengan self eksplisit tetap setara dengan bentuk titik dua.

Referensi#