Acceso mediante Closures
Parte de la sección Programación Orientada a Objetos del Journey de Lua de Coddy. Lección 45 de 70.
Las variables locales privadas son inútiles si nunca puedes interactuar con ellas. La solución es definir métodos getter y setter dentro del constructor, donde tienen acceso al ámbito del cierre.
Como estos métodos se crean dentro de :new(), pueden «ver» las variables locales. Se los asignas directamente al objeto:
local Counter = {}
Counter.__index = Counter
function Counter:new()
local count = 0 -- Variable privada
local obj = {}
-- Define métodos dentro del constructor
function obj:getCount()
return count
end
function obj:increment()
count = count + 1
end
setmetatable(obj, Counter)
return obj
endAhora se puede acceder a la variable privada count, pero solo a través de los métodos que proporcionas:
local c = Counter:new()
print(c:getCount()) -- 0
c:increment()
c:increment()
print(c:getCount()) -- 2
print(c.count) -- nil (¡todavía oculto!)Observa que :getCount() y :increment() están definidos directamente en obj, no en la tabla de la clase. Cada instancia obtiene su propia copia de estas funciones, y cada una tiene acceso a su propio count privado. Esta es la contrapartida: la privacidad real cuesta un poco más de memoria, ya que los métodos no se comparten a través del prototipo.
Desafío
Fácil¡Construyamos una clase Temperature que use la privacidad basada en cierres para almacenar un valor de temperatura en Celsius, con métodos getter y setter definidos dentro del constructor para proporcionar un acceso controlado!
Organizarás tu código en dos archivos:
Temperature.lua: crea una clase en la que el valor de la temperatura se almacene en una variable local dentro del constructor: completamente oculta al acceso externo. El constructor:new(initialCelsius)debe almacenar la temperatura en una variable local y después definir estos métodos directamente en la instancia del objeto::getCelsius(): devuelve la temperatura actual en Celsius:setCelsius(value): actualiza la temperatura a un valor nuevo:getFahrenheit(): devuelve la temperatura convertida a Fahrenheit usando la fórmula:(celsius * 9/5) + 32
main.lua: requiere tu módulo Temperature y lee dos valores de la entrada: una temperatura inicial y una nueva temperatura que establecer. Crea una instancia de Temperature con el valor inicial, imprime las lecturas en Celsius y Fahrenheit, después usa el setter para cambiar la temperatura e imprime ambas lecturas de nuevo.
Recibirás dos entradas:
- La temperatura inicial en Celsius (un número)
- Una nueva temperatura que establecer (un número)
Tu salida debe tener cuatro líneas que muestren las lecturas de temperatura antes y después del cambio:
Celsius: {initialValue}
Fahrenheit: {convertedInitial}
Celsius: {newValue}
Fahrenheit: {convertedNew}Por ejemplo, si las entradas son 0 y 100, la salida debe ser:
Celsius: 0
Fahrenheit: 32
Celsius: 100
Fahrenheit: 212Recuerda: el valor de la temperatura debe almacenarse en una variable local dentro de :new(), no en self. Los métodos getter y setter se definen dentro del constructor, donde pueden acceder a esta variable privada mediante un cierre. Intentar acceder directamente a temp.celsius o temp._celsius debe devolver nil!
Pruébalo tú mismo
-- Requiere el módulo Temperature
local Temperature = require('Temperature')
-- Lee los valores de entrada
local initialTemp = tonumber(io.read())
local newTemp = tonumber(io.read())
-- TODO: Crea una instancia de Temperature con el valor inicial
-- TODO: Imprime las lecturas iniciales de Celsius y Fahrenheit
-- TODO: Usa el setter para cambiar la temperatura a newTemp
-- TODO: Imprime las lecturas actualizadas de Celsius y Fahrenheit
Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.
Todas las lecciones de Programación Orientada a Objetos
1El concepto de 'self'
Tablas con funciones'self' explícitoLa sintaxis de dos puntosPunto vs. dos puntosResumen - Punto en movimiento4Proyecto: Banco Digital
Configuración del proyectoMétodo de depósito2Patrón de prototipo de clase
El concepto de prototipoVinculación con __indexEl constructor :new()Inicialización de atributosInstancias independientesResumen - Fábrica de coches5Sobrecarga de operadores en POO
Sumar objetosRestar objetosConcatenar objetosComparar objetos (<, >)Resumen - Matemáticas de la billetera8Encapsulamiento
Convenciones de nomenclaturaClosures para privacidadAcceso mediante ClosuresTablas de solo lecturaLógica de validaciónResumen - Bóveda segura11Patrones de diseño (Lite)
Funciones FactoryTabla SingletonPatrón IteradorObserver (Listener)Resumen - Logger Factory3Estado y Comportamiento de los Objetos
Variables de InstanciaMétodos GetterMétodos SetterPropiedades CalculadasFormateo de StringsComprobaciones de IgualdadResumen - Calificación del Estudiante6Fundamentos de la herencia
Configuración de la herenciaHerencia de métodosExtender el constructorAñadir métodos hijosCompartido vs. ÚnicoResumen: Jerarquía de formasPractica por tu cuenta: Compilador de Lua online