Tabla Singleton
Parte de la sección Programación Orientada a Objetos del Journey de Lua de Coddy — lección 63 de 70.
A veces necesitas exactamente una instancia de algo en todo tu programa: un gestor de configuración, un sistema de registro o los ajustes de un juego. Crear múltiples copias causaría problemas: ¿cuál de ellas tiene los datos correctos? El patrón Singleton asegura que solo exista una instancia.
En Lua, implementar un Singleton es sorprendentemente sencillo gracias a cómo funcionan los módulos. Cuando usas require para un módulo, Lua guarda el resultado en caché. Las llamadas posteriores a require devuelven la tabla almacenada en caché, no una nueva.
-- GameConfig.lua
local GameConfig = {
volume = 80,
difficulty = "normal"
}
function GameConfig:setVolume(v)
self.volume = v
end
return GameConfig
Ahora, en cualquier parte de tu programa:
local config1 = require("GameConfig")
local config2 = require("GameConfig")
config1:setVolume(50)
print(config2.volume) -- 50 (¡misma tabla!)
Tanto config1 como config2 hacen referencia a la misma tabla exacta. Los cambios realizados a través de una variable son visibles a través de la otra. Esto sucede automáticamente porque el require de Lua almacena el valor devuelto en package.loaded y lo reutiliza.
A diferencia del patrón Factory que crea nuevos objetos, un Singleton garantiza un estado compartido en toda su aplicación. Esto es ideal para configuraciones globales a las que múltiples partes de su código necesitan acceder y modificar de manera consistente.
Desafío
Fácil¡Vamos a construir un gestor de configuración de juego usando el patrón Singleton! Crearás un módulo de configuración que garantiza que cada parte de tu programa acceda exactamente a los mismos ajustes, sin importar cuántas veces sea requerido.
Organizarás tu código en dos archivos:
GameSettings.lua: Crea un módulo Singleton que almacene la configuración del juego. Tu tabla de ajustes debe incluirmusicVolume(empezando en 70),sfxVolume(empezando en 100) ydifficulty(empezando en"normal"). Añade tres métodos::setMusicVolume(value),:setSfxVolume(value)y:setDifficulty(level)para modificar estos ajustes. ¡Recuerda devolver la tabla al final del módulo!main.lua: Demuestra que el Singleton funciona requiriendo tu módulo GameSettings dos veces en variables diferentes. Usa la primera variable para cambiar el volumen de la música y la dificultad. Luego usa la segunda variable para imprimir los tres ajustes; si el Singleton funciona correctamente, ambas variables hacen referencia a la misma tabla, por lo que los cambios realizados a través de una son visibles a través de la otra.
Recibirás dos entradas:
- Nuevo volumen de música (un número)
- Nuevo nivel de dificultad (una cadena como
"easy"o"hard")
Después de modificar los ajustes a través de la primera variable, imprime los tres ajustes usando la segunda variable, cada uno en su propia línea en este orden: volumen de música, volumen de sfx y luego dificultad.
Por ejemplo, si las entradas son 50 y hard, la salida debería ser:
50
100
hardSi las entradas son 25 y easy, la salida debería ser:
25
100
easyObserva que el volumen de sfx permanece en su valor predeterminado de 100 ya que nunca lo cambiamos, pero el volumen de la música y la dificultad reflejan los cambios realizados a través de la primera variable, ¡lo que demuestra que ambas variables apuntan a la misma instancia Singleton!
Pruébalo tú mismo
-- main.lua - Demostrar el patrón Singleton
-- Leer las entradas
local newMusicVolume = tonumber(io.read())
local newDifficulty = io.read()
-- TODO: Requerir el módulo GameSettings en una variable (por ejemplo, settings1)
-- TODO: Requerir el módulo GameSettings OTRA VEZ en una variable DIFERENTE (por ejemplo, settings2)
-- (¡Si Singleton funciona correctamente, ambas variables hacen referencia a la misma tabla!)
-- TODO: Usar la PRIMERA variable para:
-- - Establecer el volumen de la música a newMusicVolume
-- - Establecer la dificultad a newDifficulty
-- TODO: Usar la SEGUNDA variable para imprimir la configuración
-- Imprimir cada uno en su propia línea: musicVolume, sfxVolume, difficulty
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 formas