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 la configuración del juego. Crear varias copias causaría problemas: ¿cuál tiene los datos correctos? El patrón Singleton garantiza 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 cargar un módulo, Lua almacena en caché el resultado. 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 (¡la misma tabla!)
Tanto config1 como config2 hacen referencia exactamente a la misma tabla. Los cambios realizados mediante una variable son visibles a través de la otra. Esto sucede automáticamente porque require almacena el valor devuelto en package.loaded y lo reutiliza.
A diferencia del patrón Factory, que crea objetos nuevos, un Singleton garantiza un estado compartido en toda tu aplicación. Esto es ideal para configuraciones globales a las que varias partes de tu código necesitan acceder y que deben modificar de forma coherente.
Desafío
Fácil¡Construyamos un gestor de configuración del 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 la misma configuración, sin importar cuántas veces se requiera.
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 configuración debe incluirmusicVolume(con un valor inicial de 70),sfxVolume(con un valor inicial de 100) ydifficulty(con un valor inicial de"normal"). Añade tres métodos::setMusicVolume(value),:setSfxVolume(value)y:setDifficulty(level)para modificar esta configuración. ¡Recuerda devolver la tabla al final del módulo!main.lua: Demuestra que 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. Después usa la segunda variable para imprimir las tres configuraciones. Si Singleton funciona correctamente, ambas variables hacen referencia a la misma tabla, por lo que los cambios realizados a través de una serán visibles a través de la otra.
Recibirás dos entradas:
- Nuevo volumen de la música (un número)
- Nuevo nivel de dificultad (una cadena como
"easy"o"hard")
Después de modificar la configuración a través de la primera variable, imprime las tres configuraciones usando la segunda variable, cada una en su propia línea y en este orden: volumen de la música, volumen de los efectos de sonido y después 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 los efectos de sonido 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 de Singleton!
Pruébalo tú mismo
-- main.lua - Demuestra el patrón Singleton
-- Leer entradas
local newMusicVolume = tonumber(io.read())
local newDifficulty = io.read()
-- TODO: Requiere el módulo GameSettings en una variable (p. ej., settings1)
-- TODO: Requiere el módulo GameSettings OTRA VEZ en una variable DIFERENTE (p. ej., settings2)
-- (¡Si Singleton funciona correctamente, ambas variables referencian la misma tabla!)
-- TODO: Usa la PRIMERA variable para:
-- - Establece el volumen de la música a newMusicVolume
-- - Establece la dificultad a newDifficulty
-- TODO: Usa la SEGUNDA variable para imprimir la configuración
-- Imprime 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 formasPractica por tu cuenta: Compilador de Lua online