Tabelas Somente Leitura
Parte da seção Programação Orientada a Objetos do Journey de Lua da Coddy. Lição 46 de 70.
Closures fornecem privacidade verdadeira, mas exigem a definição de métodos dentro do construtor. Há outra abordagem para um caso de uso diferente: tornar um objeto somente leitura após a criação. Isso é útil quando você quer 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 fazê-lo bloquear 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
endO truque é usar uma tabela proxy. Os dados reais ficam em data, enquanto o usuário interage com a proxy vazia.
Ao ler, __index busca em 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 objeto imutávelEsse 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ê simplesmente não pode alterá-lo.
Desafio
FácilVamos criar uma classe ImmutableConfig que cria objetos de configuração somente leitura! Depois de criados, esses objetos de configuração devem permitir a leitura de suas configurações, mas bloquear completamente qualquer tentativa de modificá-las.
Você organizará seu código em dois arquivos:
ImmutableConfig.lua: crie um módulo que produza 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 permita ler todos os campos, mas impeça qualquer modificação. Quando alguém tentar alterar um valor, deve imprimirError: Config is read-onlyem vez de gerar um erro de fato.main.lua: coloque seu ImmutableConfig em funcionamento! Leia três entradas para os valores de configuração, crie um objeto de configuração e demonstre que ele funciona corretamente:- Imprima os três valores originais
- Tente modificar o campo
maxUsers(o que deve disparar a mensagem de erro) - Imprima
maxUsersnovamente para provar que ele não foi realmente alterado
Você receberá três entradas:
- O nome da aplicação (uma string)
- A versão (uma string)
- O número máximo de usuários (um número)
Sua saída deve mostrar os valores da configuração, o erro quando a modificação for 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 deverá ser:
App: MyApp
Version: 2.0
Max Users: 100
Error: Config is read-only
Max Users: 100Lembre-se: o padrão proxy usa uma tabela vazia com a qual o usuário interage, enquanto os dados reais ficam em uma tabela interna separada. O metamétodo __index busca dados na tabela de dados, e __newindex intercepta (e bloqueia) qualquer tentativa de escrita!
Experimente você mesmo
-- Importar o módulo ImmutableConfig
local ImmutableConfig = require('ImmutableConfig')
-- Ler as entradas
local appName = io.read()
local version = io.read()
local maxUsers = tonumber(io.read())
-- TODO: Crie um objeto de configuração usando ImmutableConfig.new()
-- TODO: Imprima todos os três valores originais no formato:
-- App: {appName}
-- Version: {version}
-- Max Users: {maxUsers}
-- TODO: Tente modificar o campo maxUsers (isso deve acionar a mensagem de erro)
-- TODO: Imprima maxUsers novamente para provar que não foi alterado
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
1O Conceito 'Self'
Tabelas com Funções'self' ExplícitoA Sintaxe de Dois-pontosPonto vs Dois-pontosRecapitulação - Movendo o Ponto4Projeto: Banco Digital
Configuração do ProjetoMétodo de Depósito7Polimorfismo e Sobrescrita
Sobrescrita de MétodosChamando Métodos da Classe PaiDuck TypingInterface ComumVerificando o TipoRecapitulação - Papéis de Funcionários10Projeto: Gerenciador de Formas
Configuração do ProjetoClasse RetânguloClasse CírculoMétodo PerímetroColeção de FormasÁrea TotalFiltrar Formas2Padrão de Protótipo de Classe
O Conceito de ProtótipoVinculando com __indexO Construtor :new()Inicializando AtributosInstâncias IndependentesRecapitulação - Fábrica de Carros5Sobrecarga de Operadores em POO
Somando ObjetosSubtraindo ObjetosConcatenando ObjetosComparando Objetos (<, >)Recapitulação - Matemática de Carteira8Encapsulamento
Convenções de NomenclaturaClosures para PrivacidadeAcesso via ClosuresTabelas Somente LeituraLógica de ValidaçãoRecapitulação - Cofre Seguro11Padrões de Projeto (Lite)
Funções FactoryTabela SingletonPadrão IteratorObserver (Listener)Recapitulação - Logger Factory3Estado e Comportamento do Objeto
Variáveis de InstânciaMétodos GetterMétodos SetterPropriedades CalculadasFormatação de StringsVerificações de IgualdadeRecapitulação - Nota do Aluno6Fundamentos de Herança
Configurando a HerançaHerdando MétodosEstendendo o ConstrutorAdicionando Métodos FilhosCompartilhado vs ÚnicoRecapitulação - Hierarquia de FormasPratique por conta própria: Compilador de Lua online