Tablas de solo lectura
Parte de la sección Object Oriented Programming del Journey de Lua de Coddy — lección 46 de 70.
Los closures proporcionan una verdadera privacidad, pero requieren definir métodos dentro del constructor. Existe otro enfoque para un caso de uso diferente: hacer que un objeto sea de solo lectura después de su creación. Esto es útil cuando se desea que los campos sean visibles pero inalterables, como las coordenadas de un punto fijo.
El metamétodo __newindex intercepta los intentos de asignar nuevos valores a una tabla. Al hacer que bloquee todas las asignaciones, puedes congelar un objeto:
local ImmutablePoint = {}
function ImmutablePoint.new(x, y)
local data = {x = x, y = y}
local proxy = {}
setmetatable(proxy, {
__index = data,
__newindex = function()
error("Cannot modify immutable object")
end
})
return proxy
endEl truco consiste en utilizar una tabla proxy. Los datos reales residen en data, mientras que el usuario interactúa con el proxy vacío.
Al leer, __index obtiene de data. Al escribir, __newindex se activa en su lugar—y lo bloqueamos:
local p = ImmutablePoint.new(10, 20)
print(p.x) -- 10 (la lectura funciona)
p.x = 50 -- Error: No se puede modificar un objeto inmutableEste patrón es valioso para objetos de configuración, constantes matemáticas o cualquier dato que nunca deba cambiar después de la inicialización. El objeto sigue siendo totalmente legible; simplemente no puedes alterarlo.
Desafío
Fácil¡Vamos a construir una clase ImmutableConfig que cree objetos de configuración de solo lectura! Una vez creados, estos objetos de configuración deben permitir la lectura de sus ajustes pero bloquear completamente cualquier intento de modificarlos.
Organizarás tu código en dos archivos:
ImmutableConfig.lua: Crea un módulo que produzca objetos de configuración congelados. Tu función.new(appName, version, maxUsers)debe almacenar estos tres valores internamente y luego devolver una tabla proxy que permita leer todos los campos pero impida cualquier modificación. Cuando alguien intente cambiar un valor, debe imprimirError: Config is read-onlyen lugar de lanzar un error real.main.lua: ¡Dale vida a tu ImmutableConfig! Lee tres entradas para los valores de configuración, crea un objeto de configuración y luego demuestra que funciona correctamente:- Imprimiendo los tres valores originales
- Intentando modificar el campo
maxUsers(lo que debería activar el mensaje de error) - Imprimiendo
maxUsersde nuevo para demostrar que no se cambió realmente
Recibirás tres entradas:
- El nombre de la aplicación (una cadena)
- La versión (una cadena)
- El número máximo de usuarios (un número)
Tu salida debe mostrar los valores de configuración, el error cuando se intenta la modificación y la confirmación de que el valor permaneció sin cambios:
App: {appName}
Version: {version}
Max Users: {maxUsers}
Error: Config is read-only
Max Users: {maxUsers}Por ejemplo, si las entradas son MyApp, 2.0, y 100, la salida debe ser:
App: MyApp
Version: 2.0
Max Users: 100
Error: Config is read-only
Max Users: 100Recuerda: el patrón proxy utiliza una tabla vacía con la que el usuario interactúa, mientras que los datos reales residen en una tabla interna separada. ¡El metamétodo __index recupera los datos de la tabla interna, y __newindex intercepta (y bloquea) cualquier intento de escritura!
Hoja de referencia
El metamétodo __newindex intercepta los intentos de asignar nuevos valores a una tabla. Al bloquear todas las asignaciones, puedes crear objetos inmutables (de solo lectura):
local ImmutablePoint = {}
function ImmutablePoint.new(x, y)
local data = {x = x, y = y}
local proxy = {}
setmetatable(proxy, {
__index = data,
__newindex = function()
error("Cannot modify immutable object")
end
})
return proxy
endEl patrón de proxy utiliza una tabla vacía (proxy) con la que el usuario interactúa, mientras que los datos reales residen en una tabla interna separada (data):
__indexrecupera valores de la tabladataal realizar una lectura__newindexintercepta los intentos de escritura y los bloquea
local p = ImmutablePoint.new(10, 20)
print(p.x) -- 10 (la lectura funciona)
p.x = 50 -- Error: Cannot modify immutable objectEste patrón es útil para objetos de configuración, constantes matemáticas o cualquier dato que deba permanecer sin cambios después de su inicialización.
Pruébalo tú mismo
-- Importar el módulo ImmutableConfig
local ImmutableConfig = require('ImmutableConfig')
-- Leer entradas
local appName = io.read()
local version = io.read()
local maxUsers = tonumber(io.read())
-- TODO: Crear un objeto de configuración usando ImmutableConfig.new()
-- TODO: Imprimir los tres valores originales en el formato:
-- App: {appName}
-- Version: {version}
-- Max Users: {maxUsers}
-- TODO: Intentar modificar el campo maxUsers (esto debería activar un mensaje de error)
-- TODO: Imprimir maxUsers de nuevo para demostrar que no fue cambiado
Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.
Todas las lecciones de Object Oriented Programming
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