Menu
Coddy logo textTech

Closures para Privacidade

Parte da seção Programação Orientada a Objetos do Journey de Lua da Coddy. Lição 44 de 70.

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

A ideia principal é que as variáveis locais dentro de uma função só podem ser acessadas dentro dessa função e de quaisquer funções definidas dentro dela. Isso é chamado de closure:

local Counter = {}
Counter.__index = Counter

function Counter:new()
    local count = 0  -- Realmente privado! Não 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 não há como acessá-lo de fora: myCounter.count retorna nil, e não há um campo prefixado com sublinhado para contornar isso.

A variável count vive no escopo da closure. Ela permanece completamente oculta do código externo. (Da forma como está escrito, nada captura count ainda; na próxima lição, você adicionará métodos dentro do construtor que criam uma closure sobre ela, fazendo com que ela persista enquanto o objeto existir.) Diferentemente de self._count, que é apenas uma sugestão educada, essa abordagem torna os dados genuinamente inacessíveis.

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

challenge icon

Desafio

Fácil

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

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

  • SecretKeeper.lua: crie uma classe na qual o segredo seja armazenado em uma variável local dentro do construtor, e não em self. O construtor :new(secretValue) deve armazenar o segredo em uma variável local que exista apenas dentro da closure. Por enquanto, o objeto não terá nenhuma forma de acessar esse segredo (isso será apresentado 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: exija 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 é realmente 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 a ser armazenado (por exemplo, TopSecret123)

Sua saída deve conter 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 do segredo que você fornecer, pois 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 closures é fundamentalmente diferente da convenção de nomenclatura com sublinhado.

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

Name: Keeper
secret: nil
_secret: nil

Experimente você mesmo

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

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

-- TODO: Crie uma instância de SecretKeeper com o valor secreto

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

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

-- TODO: Imprima 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 Programação Orientada a Objetos

Pratique por conta própria: Compilador de Lua online