Menu
Coddy logo textTech

Acesso via Closures

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

Variáveis locais privadas são inúteis se você nunca puder interagir com elas. A solução é definir métodos getter e setter dentro do construtor—onde eles têm acesso ao escopo da closure.

Como esses métodos são criados dentro de :new(), eles podem "ver" as variáveis locais. Você os atribui diretamente ao objeto:

local Counter = {}
Counter.__index = Counter

function Counter:new()
    local count = 0  -- Variável privada
    
    local obj = {}
    
    -- Define métodos dentro do construtor
    function obj:getCount()
        return count
    end
    
    function obj:increment()
        count = count + 1
    end
    
    setmetatable(obj, Counter)
    return obj
end

Agora a variável privada count pode ser acessada — mas apenas através dos métodos que você fornece:

local c = Counter:new()
print(c:getCount())  -- 0
c:increment()
c:increment()
print(c:getCount())  -- 2
print(c.count)       -- nil (ainda oculto!)

Observe que :getCount() e :increment() são definidos diretamente em obj, não na tabela da classe. Cada instância recebe sua própria cópia dessas funções, cada uma com acesso ao seu próprio count privado. Este é o trade-off: a verdadeira privacidade custa um pouco mais de memória, já que os métodos não são compartilhados através do protótipo.

challenge icon

Desafio

Fácil

Vamos construir uma classe Temperature que utiliza privacidade baseada em fechamento (closure) para armazenar um valor de temperatura em Celsius, com métodos getter e setter definidos dentro do construtor para fornecer acesso controlado!

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

  • Temperature.lua: Crie uma classe onde o valor da temperatura é armazenado em uma variável local dentro do construtor — completamente oculta do acesso externo. O construtor :new(initialCelsius) deve armazenar a temperatura em uma variável local e, em seguida, definir estes métodos diretamente na instância do objeto:
    • :getCelsius() — retorna a temperatura atual em Celsius
    • :setCelsius(value) — atualiza a temperatura para um novo valor
    • :getFahrenheit() — retorna a temperatura convertida para Fahrenheit usando a fórmula: (celsius * 9/5) + 32
  • main.lua: Requeira seu módulo Temperature e leia dois valores da entrada: uma temperatura inicial e uma nova temperatura para definir. Crie uma instância de Temperature com o valor inicial, imprima as leituras em Celsius e Fahrenheit, use o setter para alterar a temperatura e imprima ambas as leituras novamente.

Você receberá duas entradas:

  1. A temperatura inicial em Celsius (um número)
  2. Uma nova temperatura para definir (um número)

Sua saída deve ter quatro linhas mostrando as leituras de temperatura antes e depois da alteração:

Celsius: {initialValue}
Fahrenheit: {convertedInitial}
Celsius: {newValue}
Fahrenheit: {convertedNew}

Por exemplo, se as entradas forem 0 e 100, a saída deve ser:

Celsius: 0
Fahrenheit: 32
Celsius: 100
Fahrenheit: 212

Lembre-se: o valor da temperatura deve ser armazenado em uma variável local dentro de :new(), não em self. Os métodos getter e setter são definidos dentro do construtor, onde podem acessar essa variável privada através de closure. Tentar acessar temp.celsius ou temp._celsius diretamente deve retornar nil!

Folha de consulta

Para criar variáveis verdadeiramente privadas em classes Lua, defina métodos getter e setter dentro do construtor, onde eles têm acesso a variáveis locais por meio de closure.

Os métodos são atribuídos diretamente à instância do objeto (não à tabela da classe), dando a cada instância sua própria cópia com acesso às suas próprias variáveis privadas:

local Counter = {}
Counter.__index = Counter

function Counter:new()
    local count = 0  -- Private variable
    
    local obj = {}
    
    -- Define methods inside the constructor
    function obj:getCount()
        return count
    end
    
    function obj:increment()
        count = count + 1
    end
    
    setmetatable(obj, Counter)
    return obj
end

Uso:

local c = Counter:new()
print(c:getCount())  -- 0
c:increment()
c:increment()
print(c:getCount())  -- 2
print(c.count)       -- nil (still hidden!)

Compensação (Trade-off): Cada instância recebe sua própria cópia dos métodos (não compartilhada através do protótipo), o que consome mais memória, mas oferece privacidade real.

Experimente você mesmo

-- Importa o módulo Temperature
local Temperature = require('Temperature')

-- Lê os valores de entrada
local initialTemp = tonumber(io.read())
local newTemp = tonumber(io.read())

-- TODO: Cria uma instância de Temperature com o valor inicial

-- TODO: Imprime as leituras iniciais em Celsius e Fahrenheit

-- TODO: Usa o setter para alterar a temperatura para newTemp

-- TODO: Imprime as leituras atualizadas em Celsius e Fahrenheit
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