Encapsulación con closures
Oculte el estado privado utilizando patrones de objetos basados en closures.
Encapsulación con closures es una lección gratuita de Lua Academy en CoddyKit. Esta es la lección 4 de 4. Puedes leer la lección completa abajo gratuitamente — luego la practicas en el navegador con un editor de código integrado y un tutor de IA 24/7. Forma parte de la ruta de aprendizaje de Lua Academy, y tu progreso se sincroniza en la web y la app de CoddyKit. El curso de Lua Academy incluye 4 lecciones en total.
Objetos basados en clausuras
Una alternativa a la POO con metatablas: use una clausura para ocultar el estado. El objeto es una tabla de funciones; todas las funciones cierran sobre variables privadas. No se necesita ninguna metatabla. El estado es verdaderamente privado e inaccesible desde el exterior.
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 frente a público
Con las clausuras, la distinción entre privado y público es absoluta: las clausuras comparten la variable capturada (privada), mientras que el código externo solo ve la tabla devuelta (pública). No hay forma de acceder a las variables privadas sin pasar por la 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,
}
endClausuras compartidas
Varias funciones de la misma tabla devuelta comparten las mismas variables capturadas. Así se comparte el estado entre métodos sin exponerlo públicamente. Todos los métodos ven las mismas variables 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,
}
endClausuras frente a metatablas
Clausuras: estado verdaderamente privado, sin sobrecoste de metatablas, pero cada instancia tiene sus propias copias de las funciones (más memoria). Metatablas: métodos compartidos (menos memoria), inspeccionables con herramientas de depuración y compatibles con la herencia. Elija según sus necesidades de privacidad y el número de instancias.
-- 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) endClausuras mutables
Las clausuras sobre variables mutables permiten crear objetos con estado sin usar tablas. Una sola clausura puede conservar el estado y 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 5Constructor con validación
Los constructores basados en clausuras validan en la función de fábrica, no en un método. Las entradas no válidas hacen que la fábrica devuelva nil o un error antes de crear ningún objeto.
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 con clausuras
Un emisor de eventos implementado con clausuras: la lista de handlers es privada; add/remove/emit constituyen la 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: 42Memoización con clausuras
Un envoltorio memoize basado en clausuras: la caché es privada, la función envuelta es privada y solo se devuelve la versión memoizada.
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()Extender objetos basados en clausuras
Puede extender un objeto basado en clausuras creando otro que llame a los métodos del original. Esto se parece al envoltorio, no a la herencia: se delega en el 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,
}
endCuándo usar clausuras
Use POO basada en clausuras cuando necesite un estado verdaderamente privado que deba ser seguro, tenga pocas instancias (la memoria no sea una preocupación) o escriba código de estilo funcional. Use metatablas cuando necesite herencia, tenga muchas instancias o quiera integrarse con bibliotecas de POO.
-- 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.")Comprobación rápida
¿Cuál es la principal ventaja de los objetos basados en clausuras frente a los objetos basados en metatablas?
Repaso: clausuras para la encapsulación
Resumen:
- Patrón de clausura: la función de fábrica devuelve una tabla de métodos; el estado es una variable capturada
- Privacidad real: no hay forma de acceder externamente a las variables capturadas
- Todos los métodos devueltos comparten las mismas variables capturadas privadas
- Compromiso: más memoria por instancia a cambio de mayor privacidad
- Úselo para código crítico en materia de seguridad o diseños de estilo funcional
Preguntas frecuentes
¿La lección «Encapsulación con closures» es gratis?
Sí — el texto completo de «Encapsulación con closures» es gratis para leer aquí en la web. Para practicarla de forma interactiva (editor de código integrado y tutor de IA 24/7) y desbloquear el resto del curso de Lua Academy, actualiza a CoddyKit PRO. El curso de Lua Academy incluye 4 lecciones en total.
¿Qué aprenderé en «Encapsulación con closures»?
Oculte el estado privado utilizando patrones de objetos basados en closures. Practicas Lua Academy con código real que ejecutas directamente en el navegador, y un tutor de IA 24/7 responde tus preguntas mientras trabajas en la lección.
¿Necesito experiencia previa para empezar Lua Academy?
No se requiere experiencia previa. Lua Academy en CoddyKit está estructurado para principiantes hasta estudiantes avanzados, así que puedes empezar aquí o desde el inicio y avanzar a tu ritmo. Esta es la lección 4 de 4.
¿Cuánto tiempo toma la lección «Encapsulación con closures»?
La mayoría de las lecciones de CoddyKit toman alrededor de 5–10 minutos. Cada una es compacta e interactiva, así que avanzas constantemente y retomas exactamente por donde dejaste en la web y la app.
¿Puedo escribir y ejecutar código en esta lección de Lua Academy?
Sí. Cada lección de Lua Academy incluye un editor de código integrado, así que escribes y ejecutas código real directamente en tu navegador y obtienes retroalimentación instantánea de IA — sin configuración local necesaria.
Todas las lecciones de este curso
- Clases mediante metatablas
- Constructores y new()
- Métodos de instancia y self
- Encapsulación con closures