Menu
Coddy logo textTech

Closures para Privacidade

Parte da seção Object Oriented Programming do Journey de Lua da Coddy — lição 44 de 70.

Convenções de nomenclatura sinalizam intenção, mas elas não impedem o acesso de fato. Qualquer pessoa ainda pode escrever player._health = 999 e isso funciona. Para privacidade real, Lua oferece uma abordagem diferente: armazenar dados em variáveis locais dentro do construtor em vez de em self.

A percepção principal é que as variáveis locais dentro de uma função são acessíveis apenas dentro dessa função — e em quaisquer funções definidas dentro dela. Isso é chamado de closure:

local Counter = {}
Counter.__index = Counter

function Counter:new()
    local count = 0  -- Verdadeiramente privado! Não está no self.
    
    local obj = {}
    setmetatable(obj, Counter)
    return obj
end

Neste exemplo, count existe apenas dentro da função :new(). Ele não está armazenado em obj, então não há como acessá-lo por fora—myCounter.count retorna nil, e não há nenhum campo prefixado com sublinhado para contornar.

A variável count vive no escopo da closure. Ela persiste enquanto o objeto existir, mas está completamente oculta do código externo. Diferente de self._count, que é apenas uma sugestão educada, esta abordagem torna os dados genuinamente inacessíveis.

Claro, dados ocultos não são úteis se você nunca puder interagir com eles. Na próxima lição, você aprenderá como definir métodos dentro do construtor que podem ler e modificar essas variáveis privadas — oferecendo acesso controlado enquanto mantém o verdadeiro encapsulamento.

challenge icon

Desafio

Fácil

Vamos construir uma classe SecretKeeper que demonstra a verdadeira privacidade de dados usando closures! Diferente da convenção de sublinhado da lição anterior, esta abordagem torna os dados genuinamente inacessíveis de fora do objeto.

Você organizará seu código em dois arquivos:

  • SecretKeeper.lua: Crie uma classe onde o segredo é armazenado em uma variável local dentro do construtor — não em self. O construtor :new(secretValue) deve armazenar o segredo em uma variável local que existe apenas dentro da closure. Por enquanto, o objeto não terá nenhuma maneira de acessar esse segredo (isso virá na próxima lição!), mas você também deve armazenar um campo público name definido como "Keeper" na instância para que possamos verificar se o objeto funciona.
  • main.lua: Importe seu módulo SecretKeeper e leia um valor secreto da entrada. Crie uma instância de SecretKeeper com esse segredo. Em seguida, demonstre que o segredo é verdadeiramente privado imprimindo três coisas:
    • O campo name do keeper (deve imprimir Keeper)
    • O que acontece quando você tenta acessar keeper.secret (deve ser nil)
    • O que acontece quando você tenta acessar keeper._secret (também deve ser nil)

Você receberá uma entrada:

  1. O valor secreto para armazenar (ex: TopSecret123)

Sua saída deve ter três linhas mostrando que, embora o campo público funcione, o segredo está completamente oculto:

Name: Keeper
secret: nil
_secret: nil

A saída será a mesma independentemente de qual segredo você passar — porque não há como acessá-lo! A variável local dentro do construtor é invisível para o mundo exterior. Isso prova que a privacidade baseada em closure é fundamentalmente diferente da convenção de nomenclatura com sublinhado.

Por exemplo, se a entrada for MyPassword, a saída ainda deve ser:

Name: Keeper
secret: nil
_secret: nil

Folha de consulta

Para criar dados verdadeiramente privados em Lua, armazene-os em variáveis locais dentro do construtor em vez de em self. Isso utiliza um closure—variáveis locais são acessíveis apenas dentro da função onde foram definidas e em quaisquer funções definidas dentro dela.

Exemplo de uma classe com dados privados:

local Counter = {}
Counter.__index = Counter

function Counter:new()
    local count = 0  -- Verdadeiramente privado! Não está em self.
    
    local obj = {}
    setmetatable(obj, Counter)
    return obj
end

Neste exemplo, count existe apenas dentro da função :new(). Ele não é armazenado em obj, então myCounter.count retorna nil. Não há como acessá-lo por fora—ao contrário de self._count (que é apenas uma convenção de nomenclatura), essa abordagem torna os dados genuinamente inacessíveis.

A variável persiste enquanto o objeto existir, mas permanece completamente oculta do código externo, proporcionando um encapsulamento verdadeiro.

Experimente você mesmo

-- Requer o módulo SecretKeeper
local SecretKeeper = require('SecretKeeper')

-- Lê o valor secreto da entrada
local secretValue = io.read()

-- TODO: Criar uma instância do SecretKeeper com o valor secreto

-- TODO: Imprimir o campo name do keeper (formato: "Name: <name>")

-- TODO: Imprimir o que acontece ao acessar keeper.secret (formato: "secret: <value>")

-- TODO: Imprimir o que acontece ao acessar keeper._secret (formato: "_secret: <value>")
quiz iconTeste seus conhecimentos

Esta lição inclui um quiz rápido. Comece a lição para respondê-lo e acompanhar seu progresso.

Todas as lições de Object Oriented Programming