Таблицы только для чтения
Часть раздела Объектно-ориентированное программирование путешествия по Lua на Coddy. Урок 46 из 70.
Замыкания обеспечивают настоящую приватность, но требуют определения методов внутри конструктора. Для другого варианта использования есть другой подход: сделать объект доступным только для чтения после создания. Это полезно, когда вы хотите, чтобы поля были видимыми, но неизменяемыми, например координаты неподвижной точки.
Метаметод __newindex перехватывает попытки присвоить таблице новые значения. Сделав так, чтобы он блокировал все присваивания, можно заморозить объект:
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
endХитрость заключается в использовании таблицы-прокси. actual data находятся в data, тогда как пользователь взаимодействует с empty proxy.
При чтении __index получает данные из data. При записи вместо него срабатывает __newindex, и мы блокируем его:
local p = ImmutablePoint.new(10, 20)
print(p.x) -- 10 (чтение работает)
p.x = 50 -- Ошибка: Нельзя изменить неизменяемый объектЭтот шаблон полезен для объектов configuration, математических констант или любых data, которые никогда не должны изменяться после инициализации. object остаётся полностью доступным для чтения. Вы просто не можете его изменить.
Задание
ЛегкоДавайте создадим класс ImmutableConfig, который создаёт конфигурационные объекты только для чтения! После создания эти объекты конфигурации должны позволять читать свои настройки, но полностью блокировать любые попытки их изменить.
Вы организуете код в двух файлах:
ImmutableConfig.lua: Создайте module, который создаёт замороженные объекты конфигурации. Ваша функция.new(appName, version, maxUsers)должна сохранить эти три значения во внутреннем хранилище, а затем вернуть proxy-таблицу, которая позволяет читать все поля, но предотвращает любые modifications. Когда кто-то пытается изменить значение, она должна вывестиError: Config is read-onlyвместо вызова actual ошибки.main.lua: Оживите ImmutableConfig! Прочитайте три входных значения для конфигурации, создайте объект config, а затем продемонстрируйте, что он работает правильно:- Выведите все три original значения
- Попытайтесь изменить поле
maxUsers(это должно вызвать сообщение об Error) - Снова выведите
maxUsers, чтобы доказать, что оно на самом деле не изменилось
Вы получите три входных значения:
- Имя application (строка)
- Версия (строка)
- Максимальное количество пользователей (число)
В вашем выводе должны отображаться значения config, Error при попытке modification и подтверждение того, что значение осталось неизменным:
App: {appName}
Version: {version}
Max Users: {maxUsers}
Error: Config is read-only
Max Users: {maxUsers}Например, если входными значениями являются MyApp, 2.0 и 100, вывод должен быть таким:
App: MyApp
Version: 2.0
Max Users: 100
Error: Config is read-only
Max Users: 100Помните: шаблон proxy использует пустую таблицу, с которой взаимодействует пользователь, а actual data находится в отдельной внутренней таблице. Метаметод __index извлекает значения из таблицы data, а __newindex перехватывает (и блокирует) любые попытки записи!
Попробуйте сами
-- Импортировать модуль ImmutableConfig
local ImmutableConfig = require('ImmutableConfig')
-- Считать входные данные
local appName = io.read()
local version = io.read()
local maxUsers = tonumber(io.read())
-- TODO: Создать объект конфигурации с помощью ImmutableConfig.new()
-- TODO: Вывести все три исходных значения в формате:
-- App: {appName}
-- Version: {version}
-- Max Users: {maxUsers}
-- TODO: Попытаться изменить поле maxUsers (это должно вызвать сообщение об ошибке)
-- TODO: Снова вывести maxUsers, чтобы доказать, что оно не изменилось
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Объектно-ориентированное программирование
1Концепция 'self'
Таблицы с функциямиЯвный 'self'Синтаксис с двоеточиемТочка против двоеточияИтоги — Движущаяся точка4Проект: Цифровой банк
Настройка проектаМетод пополнения7Полиморфизм и переопределение
Переопределение методовВызов родительских методовУтиная типизацияОбщий интерфейсПроверка типаИтоги — Роли сотрудников2Паттерн прототипа класса
Концепция прототипаСвязывание через __indexКонструктор :new()Инициализация атрибутовНезависимые экземплярыИтоги — Автозавод5Перегрузка операторов в ООП
Сложение объектовВычитание объектовКонкатенация объектовСравнение объектов (<, >)Повторение — Математика кошелька8Инкапсуляция
Соглашения об именованииЗамыкания для приватностиДоступ через замыканияТаблицы только для чтенияЛогика валидацииИтоги — Защищенное хранилище11Паттерны проектирования (Lite)
Фабричные функцииТаблица SingletonПаттерн IteratorObserver (Слушатель)Итоги — Logger Factory3Состояние и поведение объектов
Переменные экземпляраГеттерыСеттерыВычисляемые свойстваФорматирование строкПроверка на равенствоПовторение — Оценка студента6Основы наследования
Настройка наследованияНаследование методовРасширение конструктораДобавление дочерних методовОбщее и уникальноеИтоги: Иерархия фигурПотренируйтесь самостоятельно: Онлайн-компилятор Lua