Menu
Coddy logo textTech

Acceso mediante Closures

Parte de la sección Object Oriented Programming 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 closure.

Dado que estos métodos se crean dentro de :new(), pueden "ver" las variables locales. Los asignas directamente al objeto:

local Counter = {}
Counter.__index = Counter

function Counter:new()
    local count = 0  -- Variable privada
    
    local obj = {}
    
    -- Definir métodos dentro del constructor
    function obj:getCount()
        return count
    end
    
    function obj:increment()
        count = count + 1
    end
    
    setmetatable(obj, Counter)
    return obj
end

Ahora se puede acceder a la variable privada count, pero solo a través de los métodos que proporciones:

local c = Counter:new()
print(c:getCount())  -- 0
c:increment()
c:increment()
print(c:getCount())  -- 2
print(c.count)       -- nil (¡sigue 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, cada una con acceso a su propio count privado. Este es el compromiso: la verdadera privacidad cuesta un poco más de memoria, ya que los métodos no se comparten a través del prototipo.

challenge icon

Desafío

Fácil

¡Vamos a construir una clase Temperature que utilice privacidad basada en cierres (closures) 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 donde el valor de la temperatura se almacene en una variable local dentro del constructor, completamente oculta del acceso externo. El constructor :new(initialCelsius) debe almacenar la temperatura en una variable local y luego definir estos métodos directamente en la instancia del objeto:
    • :getCelsius() — devuelve la temperatura actual en Celsius
    • :setCelsius(value) — actualiza la temperatura a un nuevo valor
    • :getFahrenheit() — devuelve la temperatura convertida a Fahrenheit utilizando 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 para establecer. Crea una instancia de Temperature con el valor inicial, imprime las lecturas en Celsius y Fahrenheit, luego usa el setter para cambiar la temperatura e imprime ambas lecturas nuevamente.

Recibirás dos entradas:

  1. La temperatura inicial en Celsius (un número)
  2. Una nueva temperatura para establecer (un número)

Tu salida debe constar de 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 debería ser:

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

Recuerda: 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 a través del cierre. ¡Intentar acceder a temp.celsius o temp._celsius directamente debería devolver nil!

Hoja de referencia

Para crear variables verdaderamente privadas en las clases de Lua, define métodos getter y setter dentro del constructor donde tengan acceso a las variables locales a través de un cierre (closure).

Los métodos se asignan directamente a la instancia del objeto (no a la tabla de la clase), lo que le da a cada instancia su propia copia con acceso a sus propias variables privadas:

local Counter = {}
Counter.__index = Counter

function Counter:new()
    local count = 0  -- Variable privada
    
    local obj = {}
    
    -- Definir métodos dentro del 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 (¡sigue oculto!)

Compromiso: Cada instancia obtiene su propia copia de los métodos (no compartidos a través del prototipo), lo que consume más memoria pero proporciona una verdadera privacidad.

Pruébalo tú mismo

-- Requerir el módulo Temperature
local Temperature = require('Temperature')

-- Leer los valores de entrada
local initialTemp = tonumber(io.read())
local newTemp = tonumber(io.read())

-- TODO: Crear una instancia de Temperature con el valor inicial

-- TODO: Imprimir las lecturas iniciales en Celsius y Fahrenheit

-- TODO: Usar el setter para cambiar la temperatura a newTemp

-- TODO: Imprimir las lecturas actualizadas en Celsius y Fahrenheit
quiz iconPonte a prueba

Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.

Todas las lecciones de Object Oriented Programming