0Pricing
Lua Academy · Aula

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,
  }
end

Fechamentos 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,
  }
end

Fechamentos 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) end

Fechamentos 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  5

Construtor 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))     -- 100

Eventos 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: 42

Memoizaçã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,
  }
end

Quando 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

  1. Classes com metatabelas
  2. Construtores e new()
  3. Métodos de instância e self
  4. Encapsulamento com fechamentos
← Voltar para Lua Academy