Menu
Coddy logo textTech

Таблицы только для чтения

Часть раздела Объектно-ориентированное программирование путешествия по 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 остаётся полностью доступным для чтения. Вы просто не можете его изменить.

challenge icon

Задание

Легко

Давайте создадим класс ImmutableConfig, который создаёт конфигурационные объекты только для чтения! После создания эти объекты конфигурации должны позволять читать свои настройки, но полностью блокировать любые попытки их изменить.

Вы организуете код в двух файлах:

  • ImmutableConfig.lua: Создайте module, который создаёт замороженные объекты конфигурации. Ваша функция .new(appName, version, maxUsers) должна сохранить эти три значения во внутреннем хранилище, а затем вернуть proxy-таблицу, которая позволяет читать все поля, но предотвращает любые modifications. Когда кто-то пытается изменить значение, она должна вывести Error: Config is read-only вместо вызова actual ошибки.
  • main.lua: Оживите ImmutableConfig! Прочитайте три входных значения для конфигурации, создайте объект config, а затем продемонстрируйте, что он работает правильно:
    • Выведите все три original значения
    • Попытайтесь изменить поле maxUsers (это должно вызвать сообщение об Error)
    • Снова выведите maxUsers, чтобы доказать, что оно на самом деле не изменилось

Вы получите три входных значения:

  1. Имя application (строка)
  2. Версия (строка)
  3. Максимальное количество пользователей (число)

В вашем выводе должны отображаться значения 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, чтобы доказать, что оно не изменилось
quiz iconПроверьте себя

В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.

Все уроки раздела Объектно-ориентированное программирование

Потренируйтесь самостоятельно: Онлайн-компилятор Lua