Menu
Coddy logo textTech

Tabelas Somente Leitura

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

Closures provêem privacidade real, mas exigem a definição de métodos dentro do construtor. Existe outra abordagem para um caso de uso diferente: tornar um objeto somente leitura após a criação. Isso é útil quando você deseja que os campos sejam visíveis, mas imutáveis — como as coordenadas de um ponto fixo.

O metamétodo __newindex intercepta tentativas de atribuir novos valores a uma tabela. Ao fazer com que ele bloqueie todas as atribuições, você pode congelar um objeto:

local ImmutablePoint = {}

function ImmutablePoint.new(x, y)
    local data = {x = x, y = y}
    
    local proxy = {}
    setmetatable(proxy, {
        __index = data,
        __newindex = function()
            error("Cannot modify immutable object")
        end
    })
    
    return proxy
end

O truque é usar uma tabela proxy. Os dados reais residem em data, enquanto o usuário interage com o proxy vazio.

Ao ler, __index busca de data. Ao escrever, __newindex é acionado em vez disso — e nós o bloqueamos:

local p = ImmutablePoint.new(10, 20)
print(p.x)  -- 10 (leitura funciona)
p.x = 50    -- Erro: Não é possível modificar um objeto imutável

Este padrão é valioso para objetos de configuração, constantes matemáticas ou quaisquer dados que nunca devam mudar após a inicialização. O objeto permanece totalmente legível — você apenas não pode alterá-lo.

challenge icon

Desafio

Fácil

Vamos construir uma classe ImmutableConfig que cria objetos de configuração somente leitura! Uma vez criados, esses objetos de configuração devem permitir a leitura de suas definições, mas bloquear completamente qualquer tentativa de modificá-los.

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

  • ImmutableConfig.lua: Crie um módulo que produz objetos de configuração congelados. Sua função .new(appName, version, maxUsers) deve armazenar esses três valores internamente e, em seguida, retornar uma tabela proxy que permite a leitura de todos os campos, mas impede qualquer modificação. Quando alguém tentar alterar um valor, deve imprimir Error: Config is read-only em vez de gerar um erro real.
  • main.lua: Dê vida ao seu ImmutableConfig! Leia três entradas para os valores de configuração, crie um objeto de configuração e, em seguida, demonstre que ele funciona corretamente:
    • Imprimindo todos os três valores originais
    • Tentando modificar o campo maxUsers (o que deve disparar a mensagem de erro)
    • Imprimindo maxUsers novamente para provar que ele não foi alterado de fato

Você receberá três entradas:

  1. O nome do aplicativo (uma string)
  2. A versão (uma string)
  3. O número máximo de usuários (um número)

Sua saída deve mostrar os valores de configuração, o erro quando a modificação é tentada e a confirmação de que o valor permaneceu inalterado:

App: {appName}
Version: {version}
Max Users: {maxUsers}
Error: Config is read-only
Max Users: {maxUsers}

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

App: MyApp
Version: 2.0
Max Users: 100
Error: Config is read-only
Max Users: 100

Lembre-se: o padrão proxy usa uma tabela vazia com a qual o usuário interage, enquanto os dados reais vivem em uma tabela interna separada. O metamétodo __index busca os dados da tabela interna, e o __newindex intercepta (e bloqueia) qualquer tentativa de escrita!

Folha de consulta

O metamétodo __newindex intercepta tentativas de atribuir novos valores a uma tabela. Ao bloquear todas as atribuições, você pode criar objetos imutáveis (somente leitura):

local ImmutablePoint = {}

function ImmutablePoint.new(x, y)
    local data = {x = x, y = y}
    
    local proxy = {}
    setmetatable(proxy, {
        __index = data,
        __newindex = function()
            error("Cannot modify immutable object")
        end
    })
    
    return proxy
end

O padrão proxy usa uma tabela vazia (proxy) com a qual o usuário interage, enquanto os dados reais residem em uma tabela interna separada (data):

  • __index busca valores da tabela data ao ler
  • __newindex intercepta tentativas de escrita e as bloqueia
local p = ImmutablePoint.new(10, 20)
print(p.x)  -- 10 (reading works)
p.x = 50    -- Error: Cannot modify immutable object

Este padrão é útil para objetos de configuração, constantes matemáticas ou quaisquer dados que devam permanecer inalterados após a inicialização.

Experimente você mesmo

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

-- Lê as entradas
local appName = io.read()
local version = io.read()
local maxUsers = tonumber(io.read())

-- TODO: Cria um objeto de configuração usando ImmutableConfig.new()

-- TODO: Imprime todos os três valores originais no formato:
-- App: {appName}
-- Versão: {version}
-- Máximo de Usuários: {maxUsers}

-- TODO: Tenta modificar o campo maxUsers (isso deve disparar uma mensagem de erro)

-- TODO: Imprime maxUsers novamente para provar que não foi alterado
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