0Pricing
Lua Academy · Pelajaran

Enkapsulasi dengan Closure

Sembunyikan keadaan privat menggunakan pola objek berbasis closure.

Enkapsulasi dengan Closure adalah pelajaran Lua Academy gratis di CoddyKit. Ini adalah pelajaran 4 dari 4. Kamu bisa membaca pelajaran lengkapnya di bawah secara gratis — lalu praktikkan langsung di browser dengan editor kode bawaan dan tutor AI 24/7. Ini adalah bagian dari jalur belajar Lua Academy, dan progresmu tersinkronisasi di web dan aplikasi CoddyKit. Kursus Lua Academy mencakup 4 pelajaran total.

Objek Berbasis Closure

Alternatif OOP berbasis metatabel: gunakan closure untuk menyembunyikan keadaan. Objeknya berupa tabel fungsi; semua fungsi menutup variabel privat. Tidak diperlukan metatabel. Keadaan benar-benar privat dan tidak dapat dijangkau dari luar.

local function makeCounter(start)
  local count = start or 0   -- truly private
  return {
    increment = function() count=count+1 end,
    decrement = function() count=count-1 end,
    get       = function() return count end,
    reset     = function() count=0 end,
  }
end

local c = makeCounter(10)
c.increment(); c.increment()
print(c.get())   -- 12
print(c.count)   -- nil (truly private!)

Privat vs Publik

Dengan closure, perbedaan antara privat dan publik bersifat mutlak: closure berbagi upvalue (privat), sedangkan kode eksternal hanya melihat tabel yang dikembalikan (publik). Tidak ada cara untuk mengakses variabel privat tanpa melalui API publik.

local function makeAccount(initial)
  local balance = initial
  local transactions = {}
  
  local function record(type, amount)
    transactions[#transactions+1] = {type=type,amount=amount}
  end
  
  return {
    deposit  = function(n) record("D",n); balance=balance+n end,
    withdraw = function(n)
      if n>balance then error("insufficient funds") end
      record("W",n); balance=balance-n
    end,
    balance  = function() return balance end,
    history  = function() return transactions end,
  }
end

Closure Bersama

Beberapa fungsi dalam tabel yang sama-sama dikembalikan berbagi upvalue yang sama. Dengan cara inilah keadaan dibagikan di antara metode tanpa mengeksposnya kepada publik. Semua metode melihat variabel privat yang sama.

local function makeQueue()
  local items = {}  -- shared by all methods
  local size = 0
  return {
    enqueue = function(v) size=size+1; items[size]=v end,
    dequeue = function()
      if size==0 then return nil end
      local head=1
      while items[head]==nil do head=head+1 end
      local v=items[head]; items[head]=nil; return v
    end,
    isEmpty = function() return size==0 end,
    count   = function() return size end,
  }
end

Closure vs Metatabel

Closure: keadaan benar-benar privat, tanpa biaya tambahan metatabel, setiap instans memiliki salinan fungsi sendiri (lebih banyak memori). Metatabel: metode dibagikan (lebih sedikit memori), dapat diperiksa dengan alat debug, dan mendukung pewarisan. Pilih berdasarkan kebutuhan privasi dan jumlah instans.

-- Closure: per-instance functions (more memory)
local function makePoint(x,y)
  return {
    x = function() return x end,
    y = function() return y end,
    distance = function()
      return math.sqrt(x*x+y*y)
    end
  }
end

-- Metatable: shared methods (less memory)
-- local Point = {}; Point.__index = Point
-- function Point.new(x,y) return setmetatable({x=x,y=y},Point) end

Closure yang Dapat Diubah

Closure atas variabel yang dapat diubah memungkinkan Anda membuat objek berkeadaan tanpa tabel sama sekali. Satu closure dapat menyimpan keadaan dan menghitung hasil.

local function makeAccumulator()
  local sum, count = 0, 0
  return {
    add = function(n) sum=sum+n; count=count+1 end,
    mean = function()
      return count>0 and sum/count or 0
    end,
    total = function() return sum end,
    n     = function() return count end,
  }
end

local acc = makeAccumulator()
for _, v in ipairs({10,20,30,40,50}) do
  acc.add(v)
end
print(acc.total(),acc.mean(),acc.n())  -- 150  30.0  5

Konstruktor dengan Validasi

Konstruktor berbasis closure melakukan validasi dalam fungsi pabrik, bukan dalam metode. Masukan yang tidak valid menyebabkan pabrik mengembalikan nil atau galat sebelum objek apa pun dibuat.

local function makeRange(lo, hi)
  assert(lo<=hi,"lo must be <= hi")
  return {
    contains = function(v) return v>=lo and v<=hi end,
    clamp    = function(v) return math.max(lo,math.min(hi,v)) end,
    lo       = function() return lo end,
    hi       = function() return hi end,
    span     = function() return hi-lo end,
  }
end

local r = makeRange(0,100)
print(r.contains(50))   -- true
print(r.clamp(150))     -- 100

Peristiwa dengan Closure

Pemancar peristiwa yang diimplementasikan dengan closure: daftar penangan bersifat privat; add/remove/emit adalah API publiknya.

local function makeEmitter()
  local handlers = {}
  return {
    on = function(event, fn)
      handlers[event] = handlers[event] or {}
      handlers[event][#handlers[event]+1] = fn
    end,
    emit = function(event, ...)
      for _, fn in ipairs(handlers[event] or {}) do
        fn(...)
      end
    end,
    off = function(event) handlers[event] = {} end,
  }
end

local emitter = makeEmitter()
emitter.on("data", function(v) print("Got:", v) end)
emitter.emit("data", 42)   -- Got: 42

Memoisasi Closure

Pembungkus memoize yang menggunakan closure: tembolok bersifat privat, fungsi yang dibungkus juga privat, dan hanya versi yang telah dimemoisasi yang dikembalikan.

local function memoize(fn)
  local cache = {}
  return function(...)
    local key = table.concat({...}, ",")
    if cache[key] == nil then
      cache[key] = fn(...)
    end
    return cache[key]
  end
end

local expensiveFib
expensiveFib = memoize(function(n)
  if n<=1 then return n end
  return expensiveFib(n-1)+expensiveFib(n-2)
end)

for i=0,10 do io.write(expensiveFib(i).." ") end
print()

Memperluas Objek Closure

Anda dapat memperluas objek berbasis closure dengan membuat objek baru yang memanggil metode milik objek asli. Ini seperti pembungkusan, bukan pewarisan — objek asli didelegasikan.

local function makeTimedCounter(start)
  local base = makeCounter and makeCounter(start) or (function()
    local c=start or 0
    return {inc=function()c=c+1 end,get=function()return c end}
  end)()
  local callCount = 0
  return {
    increment = function()
      callCount=callCount+1
      base.inc()
    end,
    get = base.get,
    calls = function() return callCount end,
  }
end

Kapan Menggunakan Closure

Gunakan OOP berbasis closure ketika: Anda membutuhkan keadaan yang benar-benar privat dan harus aman, hanya memiliki sedikit instans (memori bukan masalah), atau sedang menulis kode bergaya fungsional. Gunakan metatabel ketika: Anda membutuhkan pewarisan, memiliki banyak instans, atau ingin berintegrasi dengan pustaka OOP.

-- Guideline summary:
-- Closures: true privacy, no metatable, self-contained
-- Metatables: shared methods, inheritance, instanceof checks

-- Rule of thumb:
-- < 100 instances with private state -> closures
-- > 100 instances OR need inheritance -> metatables

print("Both patterns are valid Lua OOP.")

Pemeriksaan Cepat

Apa keunggulan utama objek berbasis closure dibandingkan objek berbasis metatabel?

Ringkasan: Closure untuk Enkapsulasi

Ringkasan:

  • Pola closure: fungsi pabrik mengembalikan tabel metode; keadaan disimpan dalam upvalue
  • Benar-benar privat: tidak ada cara untuk mengakses upvalue dari luar
  • Semua metode yang dikembalikan berbagi upvalue privat yang sama
  • Kompromi: lebih banyak memori per instans, tetapi privasi lebih baik
  • Gunakan untuk kode yang kritis terhadap keamanan atau rancangan bergaya fungsional

Pertanyaan yang Sering Diajukan

Apakah pelajaran “Enkapsulasi dengan Closure” gratis?

Ya — teks lengkap “Enkapsulasi dengan Closure” gratis dibaca di sini di web. Untuk praktiknya secara interaktif (editor kode bawaan dan tutor AI 24/7) dan buka sisa kursus Lua Academy, upgrade ke CoddyKit PRO. Kursus Lua Academy mencakup 4 pelajaran total.

Apa yang akan aku pelajari di “Enkapsulasi dengan Closure”?

Sembunyikan keadaan privat menggunakan pola objek berbasis closure. Kamu berlatih Lua Academy dengan kode praktik yang langsung kamu jalankan di browser, dan tutor AI 24/7 menjawab pertanyaanmu saat kamu mengerjakan pelajaran ini.

Apakah aku perlu pengalaman untuk memulai Lua Academy?

Tidak diperlukan pengalaman sebelumnya. Lua Academy di CoddyKit dirancang untuk pemula hingga pelajar tingkat lanjut, jadi kamu bisa memulai di sini atau dari awal dan belajar sesuai kecepatan kamu sendiri. Ini adalah pelajaran 4 dari 4.

Berapa lama pelajaran “Enkapsulasi dengan Closure” memakan waktu?

Sebagian besar pelajaran CoddyKit memakan waktu sekitar 5–10 menit. Setiap pelajaran ringkas dan interaktif, jadi kamu membuat kemajuan stabil dan melanjutkan dari tempat kamu tinggalkan di web dan aplikasi.

Bisakah aku menulis dan menjalankan kode dalam pelajaran Lua Academy ini?

Ya. Setiap pelajaran Lua Academy menyertakan editor kode bawaan, jadi kamu menulis dan menjalankan kode nyata langsung di browser dan mendapatkan umpan balik AI instan — tidak diperlukan penyiapan lokal.

Semua pelajaran dalam kursus ini

  1. Class melalui Metatabel
  2. Konstruktor dan new()
  3. Metode Instans dan self
  4. Enkapsulasi dengan Closure
← Kembali ke Lua Academy