Menu
Coddy logo textTech

Closures para privacidad

Parte de la sección Programación Orientada a Objetos del Journey de Lua de Coddy — lección 44 de 70.

Las convenciones de nomenclatura señalan la intención, pero en realidad no impiden el acceso. Cualquiera puede seguir escribiendo player._health = 999 y funciona. Para una privacidad real, Lua ofrece un enfoque diferente: almacenar datos en variables locales dentro del constructor en lugar de en self.

La idea clave es que las variables locales dentro de una función solo son accesibles dentro de esa función —y de cualquier función definida dentro de ella—. A esto se le llama closure:

local Counter = {}
Counter.__index = Counter

function Counter:new()
    local count = 0  -- ¡Verdaderamente privado! No está en self.
    
    local obj = {}
    setmetatable(obj, Counter)
    return obj
end

En este ejemplo, count existe solo dentro de la función :new(). No se almacena en obj, por lo que no hay forma de acceder a ella desde el exterior—myCounter.count devuelve nil, y no hay ningún campo con prefijo de guion bajo para omitir.

La variable count vive en el ámbito del cierre (closure). Permanece completamente oculta para el código externo. (Tal como está escrito, nada captura a count todavía; en la siguiente lección añadirás métodos dentro del constructor que se cierren sobre ella, de modo que persista mientras el objeto lo haga). A diferencia de self._count, que es solo una sugerencia de cortesía, este enfoque hace que los datos sean genuinamente inaccesibles.

Por supuesto, los datos ocultos no son útiles si nunca puedes interactuar con ellos. En la próxima lección, aprenderás cómo definir métodos dentro del constructor que pueden leer y modificar estas variables privadas, brindándote un acceso controlado mientras mantienes una verdadera encapsulación.

challenge icon

Desafío

Fácil

¡Vamos a construir una clase SecretKeeper que demuestre la verdadera privacidad de datos utilizando cierres (closures)! A diferencia de la convención del guion bajo de la lección anterior, este enfoque hace que los datos sean genuinamente inaccesibles desde fuera del objeto.

Organizarás tu código en dos archivos:

  • SecretKeeper.lua: Crea una clase donde el secreto se almacene en una variable local dentro del constructor, no en self. El constructor :new(secretValue) debe almacenar el secreto en una variable local que exista solo dentro del cierre. Por ahora, el objeto no tendrá ninguna forma de acceder a este secreto (¡eso vendrá en la próxima lección!), pero también debes almacenar un campo público name establecido en "Keeper" en la instancia para que podamos verificar que el objeto funciona.
  • main.lua: Requiere tu módulo SecretKeeper y lee un valor secreto de la entrada. Crea una instancia de SecretKeeper con ese secreto. Luego, demuestra que el secreto es verdaderamente privado imprimiendo tres cosas:
    • El campo name del guardián (debe imprimir Keeper)
    • Qué sucede cuando intentas acceder a keeper.secret (debe ser nil)
    • Qué sucede cuando intentas acceder a keeper._secret (también debe ser nil)

Recibirás una entrada:

  1. El valor secreto a almacenar (por ejemplo, TopSecret123)

Tu salida debe constar de tres líneas que muestren que, mientras el campo público funciona, el secreto está completamente oculto:

Name: Keeper
secret: nil
_secret: nil

La salida será la misma independientemente del secreto que pases, ¡porque no hay forma de acceder a él! La variable local dentro del constructor es invisible para el mundo exterior. Esto demuestra que la privacidad basada en cierres es fundamentalmente diferente de la convención de nomenclatura con guion bajo.

Por ejemplo, si la entrada es MyPassword, la salida aún debería ser:

Name: Keeper
secret: nil
_secret: nil

Pruébalo tú mismo

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

-- Leer el valor secreto de la entrada
local secretValue = io.read()

-- TODO: Crear una instancia de SecretKeeper con el valor secreto

-- TODO: Imprimir el campo name del keeper (formato: "Name: <name>")

-- TODO: Imprimir qué sucede al acceder a keeper.secret (formato: "secret: <value>")

-- TODO: Imprimir qué sucede al acceder a keeper._secret (formato: "_secret: <value>")
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 Programación Orientada a Objetos