Incapsulamento con le closure
Nasconda lo stato privato usando pattern a oggetti basati su closure.
Incapsulamento con le closure è una lezione Lua Academy gratuita su CoddyKit. Questa è la lezione 4 di 4. Puoi leggere la lezione completa qui gratuitamente — poi esercitati direttamente nel browser con un editor di codice integrato e un tutor IA disponibile 24/7. Fa parte del percorso di apprendimento Lua Academy, e i tuoi progressi si sincronizzano tra il web e l'app CoddyKit. Il corso Lua Academy include 4 lezioni in totale.
Oggetti basati su closure
Un'alternativa all'OOP basata sulle metatabelle: utilizzi una closure per nascondere lo stato. L'oggetto è una tabella di funzioni; tutte le funzioni chiudono sulle variabili private. Non è necessaria alcuna metatabella. Lo stato è realmente privato e non raggiungibile dall'esterno.
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!)Privato e pubblico
Con le closure, la distinzione tra privato e pubblico è assoluta: le closure condividono l'upvalue (privato), mentre il codice esterno vede solo la tabella restituita (pubblico). Non è possibile accedere alle variabili private senza passare dall'API pubblica.
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,
}
endClosure condivise
Più funzioni nella stessa tabella restituita condividono gli stessi upvalue. È così che lo stato viene condiviso tra i metodi senza renderlo pubblico. Tutti i metodi vedono le stesse variabili private.
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,
}
endClosure e metatabelle
Closure: stato realmente privato, nessun sovraccarico delle metatabelle, ma ogni istanza possiede copie proprie delle funzioni (maggiore uso di memoria). Metatabelle: metodi condivisi (minore uso di memoria), ispezionabili con gli strumenti di debug e con supporto all'ereditarietà. Scelga in base alle esigenze di privacy e al numero di istanze.
-- 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) endClosure mutabili
Le closure su variabili mutabili consentono di creare oggetti con stato senza utilizzare affatto le tabelle. Una singola closure può contenere lo stato e calcolare i risultati.
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 5Costruttore con convalida
I costruttori basati su closure convalidano i dati nella funzione factory, non in un metodo. Gli input non validi fanno sì che la factory restituisca nil o un errore prima della creazione dell'oggetto.
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)) -- 100Eventi con le closure
Un event emitter implementato con le closure: l'elenco degli handler è privato; add/remove/emit costituiscono l'API pubblica.
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: 42Memoization con closure
Un wrapper memoize che utilizza le closure: la cache è privata, la funzione sottoposta al wrapping è privata e viene restituita solo la versione memoizzata.
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()Estendere gli oggetti basati su closure
Può estendere un oggetto basato su closure creandone uno nuovo che chiama i metodi dell'originale. È simile al wrapping, non all'ereditarietà: si delega all'oggetto originale.
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,
}
endQuando utilizzare le closure
Utilizzi l'OOP basata su closure quando: ha bisogno di uno stato realmente privato che debba essere sicuro, ha poche istanze (la memoria non è un problema) oppure sta scrivendo codice in stile funzionale. Utilizzi le metatabelle quando: ha bisogno dell'ereditarietà, di molte istanze oppure desidera integrarsi con librerie 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.")Verifica rapida
Qual è il principale vantaggio degli oggetti basati su closure rispetto a quelli basati su metatabelle?
Riepilogo: closure per l'incapsulamento
Riepilogo:
- Pattern delle closure: una funzione factory restituisce una tabella di metodi; lo stato è un upvalue
- Privacy completa: non è possibile accedere agli upvalue dall'esterno
- Tutti i metodi restituiti condividono gli stessi upvalue privati
- Compromesso: maggiore uso di memoria per istanza in cambio di una privacy migliore
- Utilizzi questo approccio per codice critico per la sicurezza o progetti in stile funzionale
Domande Frequenti
La lezione «Incapsulamento con le closure» è gratuita?
Sì — il testo completo di «Incapsulamento con le closure» è gratuito qui sul web. Per esercitarvi in modo interattivo (un editor di codice integrato e un tutor IA 24/7) e sbloccare il resto del corso Lua Academy, passa a CoddyKit PRO. Il corso Lua Academy include 4 lezioni in totale.
Cosa imparerò in «Incapsulamento con le closure»?
Nasconda lo stato privato usando pattern a oggetti basati su closure. Eserciti Lua Academy con codice pratico che esegui direttamente nel browser, e un tutor IA 24/7 risponde alle tue domande mentre lavori sulla lezione.
Ho bisogno di esperienza per iniziare Lua Academy?
Non è richiesta alcuna esperienza precedente. Lua Academy su CoddyKit è strutturato per principianti e studenti avanzati, quindi puoi iniziare da qui o dall'inizio e procedere al tuo ritmo. Questa è la lezione 4 di 4.
Quanto tempo richiede la lezione «Incapsulamento con le closure»?
La maggior parte delle lezioni CoddyKit richiede circa 5–10 minuti. Ogni lezione è breve e interattiva, quindi fai progressi costanti e riprendi esattamente da dove hai lasciato su web e app.
Posso scrivere ed eseguire codice in questa lezione Lua Academy?
Sì. Ogni lezione Lua Academy include un editor di codice integrato, quindi scrivi ed esegui codice reale direttamente nel tuo browser e ricevi feedback istantaneo dall'IA — nessuna configurazione locale necessaria.
Tutte le lezioni di questo corso
- Classi tramite metatabelle
- Costruttori e new()
- Metodi delle istanze e self
- Incapsulamento con le closure