Menu
Coddy logo textTech

Таблица Singleton

Часть раздела Объектно-ориентированное программирование путешествия по Lua на Coddy — урок 63 из 70.

Иногда вам нужен ровно один экземпляр чего-либо во всей программе — менеджер конфигурации, система логирования или настройки игры. Создание нескольких копий вызовет проблемы: в какой из них находятся правильные данные? Паттерн Одиночка (Singleton) гарантирует существование только одного экземпляра.

В Lua реализация Singleton удивительно проста благодаря тому, как работают модули. Когда вы вызываете require для модуля, Lua кэширует результат. Последующие вызовы require возвращают кэшированную таблицу, а не новую.

-- GameConfig.lua
local GameConfig = {
    volume = 80,
    difficulty = "normal"
}

function GameConfig:setVolume(v)
    self.volume = v
end

return GameConfig

Теперь в любом месте вашей программы:

local config1 = require("GameConfig")
local config2 = require("GameConfig")

config1:setVolume(50)
print(config2.volume)  -- 50 (та же самая таблица!)

И config1, и config2 ссылаются на одну и ту же таблицу. Изменения, внесенные через одну переменную, видны через другую. Это происходит автоматически, потому что функция Lua require сохраняет возвращаемое значение в package.loaded и использует его повторно.

В отличие от паттерна Factory, который создает новые объекты, Singleton гарантирует общее состояние во всем вашем приложении. Это идеально подходит для глобальных настроек, к которым нескольким частям вашего кода требуется доступ и которые необходимо согласованно изменять.

challenge icon

Задание

Легко

Давайте создадим менеджер настроек игры, используя паттерн Одиночка (Singleton)! Вы создадите модуль конфигурации, который гарантирует, что каждая часть вашей программы обращается к одним и тем же настройкам — независимо от того, сколько раз этот модуль будет подключен.

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

  • GameSettings.lua: Создайте модуль Singleton, который хранит конфигурацию игры. Ваша таблица настроек должна включать musicVolume (начальное значение 70), sfxVolume (начальное значение 100) и difficulty (начальное значение "normal"). Добавьте три метода: :setMusicVolume(value), :setSfxVolume(value) и :setDifficulty(level) для изменения этих настроек. Не забудьте вернуть таблицу в конце модуля!
  • main.lua: Продемонстрируйте работу Singleton, подключив модуль GameSettings дважды в разные переменные. Используйте первую переменную, чтобы изменить громкость музыки и сложность. Затем используйте вторую переменную, чтобы вывести все три настройки — если Singleton работает правильно, обе переменные ссылаются на одну и ту же таблицу, поэтому изменения, внесенные через одну, будут видны через другую.

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

  1. Новая громкость музыки (число)
  2. Новый уровень сложности (строка, например "easy" или "hard")

После изменения настроек через первую переменную выведите три настройки, используя вторую переменную, каждую на отдельной строке в следующем порядке: громкость музыки, громкость спецэффектов (sfx), затем сложность.

Например, если на вход поданы 50 и hard, вывод должен быть следующим:

50
100
hard

Если на вход поданы 25 и easy, вывод должен быть следующим:

25
100
easy

Обратите внимание, что громкость спецэффектов остается на значении по умолчанию 100, так как мы ее не меняли, но громкость музыки и сложность отражают изменения, сделанные через первую переменную, что доказывает: обе переменные указывают на один и тот же экземпляр Singleton!

Попробуйте сами

-- main.lua - Демонстрация паттерна Singleton

-- Чтение входных данных
local newMusicVolume = tonumber(io.read())
local newDifficulty = io.read()

-- TODO: Подключите модуль GameSettings в переменную (например, settings1)

-- TODO: Подключите модуль GameSettings ПОВТОРНО в ДРУГУЮ переменную (например, settings2)
-- (Если Singleton работает правильно, обе переменные будут ссылаться на одну и ту же таблицу!)

-- TODO: Используйте ПЕРВУЮ переменную, чтобы:
--   - Установить громкость музыки (music volume) в значение newMusicVolume
--   - Установить сложность (difficulty) в значение newDifficulty

-- TODO: Используйте ВТОРУЮ переменную для вывода настроек
-- Выведите каждое значение на новой строке: musicVolume, sfxVolume, difficulty
quiz iconПроверьте себя

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

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