Tablas de solo lectura
Parte de la sección Programación Orientada a Objetos del Journey de Lua de Coddy. Lección 46 de 70.
Las clausuras proporcionan privacidad real, 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 resulta útil cuando quieres que los campos sean visibles pero inmodificables, como las coordenadas de un punto fijo.
El metumé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 usar una tabla proxy. Los datos reales se encuentran en data, mientras que el usuario interactúa con el proxy vacío.
Al leer, __index obtiene datos de data. Al escribir, se activa __newindex 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 completamente legible. Simplemente no puedes modificarlo.
Desafío
Fácil¡Construyamos una clase ImmutableConfig que cree objetos de configuración de solo lectura! Una vez creados, estos objetos de configuración deberían permitir leer sus ajustes, pero bloquear por completo 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 internamente estos tres valores y, después, 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 generar un error real.main.lua: ¡Pon en marcha tu ImmutableConfig! Lee tres entradas para los valores de configuración, crea un objeto de configuración y, después, 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 en realidad no cambió
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 modificarla 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 debería 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 interactúa el usuario, mientras que los datos reales viven en una tabla interna separada. El metamétodo __index obtiene los datos de la tabla de datos, y __newindex intercepta (y bloquea) cualquier intento de escritura.
Pruébalo tú mismo
-- Importar el módulo ImmutableConfig
local ImmutableConfig = require('ImmutableConfig')
-- Leer las entradas
local appName = io.read()
local version = io.read()
local maxUsers = tonumber(io.read())
-- TODO: Crear un objeto config 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 el mensaje de error)
-- TODO: Imprimir maxUsers de nuevo para demostrar que no se modificó
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