Acesso via Closures
Parte da seção Programação Orientada a Objetos 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 do 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 os métodos dentro do construtor
function obj:getCount()
return count
end
function obj:increment()
count = count + 1
end
setmetatable(obj, Counter)
return obj
endAgora a variável privada count pode ser acessada, mas somente por meio 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. Essa é a compensação: a privacidade verdadeira consome um pouco mais de memória, pois os métodos não são compartilhados por meio do protótipo.
Desafio
FácilVamos criar uma classe Temperature que usa privacidade baseada em 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 na qual o valor da temperatura seja armazenado em uma variável local dentro do construtor: completamente oculto 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: importe seu módulo Temperature e leia dois valores da entrada: uma temperatura inicial e uma nova temperatura a ser definida. 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 novamente ambas as leituras.
Você receberá duas entradas:
- A temperatura inicial em Celsius (um número)
- Uma nova temperatura a ser definida (um número)
Sua saída deve conter quatro linhas mostrando as leituras da 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 deverá ser:
Celsius: 0
Fahrenheit: 32
Celsius: 100
Fahrenheit: 212Lembre-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 por meio de closure. Tentar acessar temp.celsius ou temp._celsius diretamente deve retornar nil!
Experimente você mesmo
-- Requer o módulo Temperature
local Temperature = require('Temperature')
-- Lê os valores de entrada
local initialTemp = tonumber(io.read())
local newTemp = tonumber(io.read())
-- TODO: Crie uma instância de Temperature com o valor inicial
-- TODO: Imprima as leituras iniciais de Celsius e Fahrenheit
-- TODO: Use o setter para alterar a temperatura para newTemp
-- TODO: Imprima as leituras atualizadas de Celsius e Fahrenheit
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