Encapsulamento com fechamentos
Oculte o estado privado usando padrões de objetos baseados em fechamentos.
Encapsulamento com fechamentos é uma aula grátis de Lua Academy no CoddyKit. Esta é a aula 4 de 4. Você pode ler a aula completa abaixo gratuitamente — depois pratica ao vivo no navegador com um editor de código integrado e um tutor de IA 24/7. Faz parte do caminho de aprendizado de Lua Academy, e seu progresso é sincronizado entre a web e o app CoddyKit. O curso de Lua Academy inclui 4 aulas no total.
Objetos Baseados em Fechamentos
Uma alternativa à OOP com metatabelas: use um fechamento para ocultar o estado. O objeto é uma tabela de funções; todas as funções capturam variáveis privadas. Não é necessária uma metatabela. O estado é realmente privado e inacessível externamente.
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!)Privado e Público
Com fechamentos, a distinção entre privado e público é absoluta: os fechamentos compartilham o valor capturado (privado), enquanto o código externo vê apenas a tabela retornada (público). Não há como acessar variáveis privadas sem passar pela API pública.
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,
}
endFechamentos Compartilhados
Várias funções na mesma tabela retornada compartilham os mesmos valores capturados. É assim que o estado é compartilhado entre métodos sem ser exposto publicamente. Todos os métodos veem as mesmas variáveis privadas.
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,
}
endFechamentos e Metatabelas
Fechamentos: estado realmente privado, sem sobrecarga de metatabela, mas cada instância tem suas próprias cópias das funções (mais memória). Metatabelas: métodos compartilhados (menos memória), inspecionáveis com ferramentas de depuração e com suporte a herança. Escolha de acordo com as necessidades de privacidade e a quantidade de instâncias.
-- 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) endFechamentos Mutáveis
Fechamentos sobre variáveis mutáveis permitem criar objetos com estado sem usar tabelas. Um único fechamento pode manter o estado e calcular resultados.
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 5Construtor com Validação
Construtores baseados em fechamentos validam na função de fábrica, não em um método. Entradas inválidas fazem a fábrica retornar nil ou um erro antes que qualquer objeto seja criado.
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)) -- 100Eventos com Fechamentos
Um emissor de eventos implementado com fechamentos: a lista de manipuladores é privada; add/remove/emit formam a API pública.
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: 42Memoização com Fechamentos
Um invólucro de memoização usando fechamentos: o cache é privado, a função envolvida é privada e somente a versão memoizada é retornada.
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()Estendendo Objetos Baseados em Fechamentos
Você pode estender um objeto baseado em fechamentos criando um novo objeto que chama os métodos do original. Isso é como encapsulamento, não herança — a delegação é feita para o objeto original.
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 Usar Fechamentos
Use OOP baseada em fechamentos quando: precisar de um estado realmente privado que deva ser protegido, tiver poucas instâncias (a memória não for uma preocupação) ou estiver escrevendo código em estilo funcional. Use metatabelas quando: precisar de herança, tiver muitas instâncias ou quiser integrar bibliotecas de 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ção Rápida
Qual é a principal vantagem dos objetos baseados em fechamentos em relação aos objetos baseados em metatabelas?
Revisão: Fechamentos para Encapsulamento
Resumo:
- Padrão de fechamento: a função de fábrica retorna uma tabela de métodos; o estado é um valor capturado
- Verdadeiramente privado: não há como acessar externamente os valores capturados
- Todos os métodos retornados compartilham os mesmos valores capturados privados
- Compromisso: mais memória por instância em troca de mais privacidade
- Use em código crítico para a segurança ou em projetos de estilo funcional
Perguntas Frequentes
A aula “Encapsulamento com fechamentos” é grátis?
Sim — o texto completo de “Encapsulamento com fechamentos” é grátis para ler aqui na web. Para praticá-la interativamente (um editor de código integrado e um tutor de IA 24/7) e desbloquear o restante do curso de Lua Academy, atualize para CoddyKit PRO. O curso de Lua Academy inclui 4 aulas no total.
O que vou aprender em “Encapsulamento com fechamentos”?
Oculte o estado privado usando padrões de objetos baseados em fechamentos. Você pratica Lua Academy com código prático que executa diretamente no navegador, e um tutor de IA 24/7 responde suas dúvidas enquanto trabalha na aula.
Preciso ter experiência prévia para começar Lua Academy?
Nenhuma experiência prévia é necessária. Lua Academy no CoddyKit é estruturado para alunos iniciantes até avançados, então você pode começar aqui ou desde o início e aprender no seu ritmo. Esta é a aula 4 de 4.
Quanto tempo leva a aula “Encapsulamento com fechamentos”?
A maioria das aulas CoddyKit leva cerca de 5–10 minutos. Cada uma é compacta e interativa, então você faz progresso constante e retoma exatamente de onde parou entre web e app.
Posso escrever e executar código nesta aula de Lua Academy?
Sim. Cada aula de Lua Academy inclui um editor de código integrado, então você escreve e executa código real direto no navegador e recebe feedback de IA instantaneamente — nenhuma configuração local necessária.
Todas as aulas deste curso
- Classes com metatabelas
- Construtores e new()
- Métodos de instância e self
- Encapsulamento com fechamentos