Таблица 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's require сохраняет возвращаемое значение в package.loaded и повторно использует его.
В отличие от паттерна Factory, который создаёт новые объекты, Singleton гарантирует общее состояние во всём вашем приложении. Это идеально подходит для глобальных настроек, к которым разные части вашего кода должны получать доступ и единообразно изменять их.
Задание
ЛегкоДавайте создадим менеджер настроек игры, используя паттерн Singleton! Вы создадите модуль конфигурации, который гарантирует, что каждая часть вашей программы обращается к одним и тем же настройкам: независимо от того, сколько раз он был подключен.
Вы организуете код в двух файлах:
GameSettings.lua: Создайте модуль Singleton, который хранит конфигурацию игры. Ваша таблица настроек должна включатьmusicVolume(начальное значение — 70),sfxVolume(начальное значение — 100) иdifficulty(начальное значение —"normal"). Добавьте три метода::setMusicVolume(value),:setSfxVolume(value)и:setDifficulty(level)для изменения этих настроек. Не забудьте вернуть таблицу в конце модуля!main.lua: Продемонстрируйте работу Singleton, подключив модуль GameSettings дважды в разные переменные. Используйте первую переменную, чтобы изменить громкость музыки и difficulty. Затем используйте вторую переменную, чтобы вывести все три настройки. Если Singleton работает правильно, обе переменные ссылаются на одну и ту же таблицу, поэтому изменения, внесённые через одну переменную, видны через другую.
Вы получите два входных значения:
- Новая громкость музыки (число)
- Новый уровень difficulty (строка, например
"easy"или"hard")
После изменения настроек через первую переменную выведите три настройки с помощью второй переменной, каждую в отдельной строке, в следующем порядке: громкость музыки, громкость звуковых эффектов, затем difficulty.
Например, если входные значения — 50 и hard, вывод должен быть таким:
50
100
hardЕсли входные значения — 25 и easy, вывод должен быть таким:
25
100
easyОбратите внимание, что громкость звуковых эффектов остаётся равной значению по умолчанию — 100, поскольку мы её не изменяли, а громкость музыки и difficulty отражают изменения, внесённые через первую переменную, доказывая, что обе переменные указывают на один и тот же экземпляр Singleton!
Попробуйте сами
-- main.lua - Демонстрация паттерна Singleton
-- Читаем входные данные
local newMusicVolume = tonumber(io.read())
local newDifficulty = io.read()
-- TODO: Подключите модуль GameSettings в переменную (например, settings1)
-- TODO: Подключите модуль GameSettings ЕЩЁ РАЗ в ДРУГУЮ переменную (например, settings2)
-- (Если Singleton работает правильно, обе переменные ссылаются на одну и ту же таблицу!)
-- TODO: Используйте ПЕРВУЮ переменную, чтобы:
-- - Установите громкость музыки в newMusicVolume
-- - Установите сложность в newDifficulty
-- TODO: Используйте ВТОРУЮ переменную для вывода настроек
-- Выведите каждое на своей строке: musicVolume, sfxVolume, difficulty
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Объектно-ориентированное программирование
1Концепция 'self'
Таблицы с функциямиЯвный 'self'Синтаксис с двоеточиемТочка против двоеточияИтоги — Движущаяся точка4Проект: Цифровой банк
Настройка проектаМетод пополнения7Полиморфизм и переопределение
Переопределение методовВызов родительских методовУтиная типизацияОбщий интерфейсПроверка типаИтоги — Роли сотрудников2Паттерн прототипа класса
Концепция прототипаСвязывание через __indexКонструктор :new()Инициализация атрибутовНезависимые экземплярыИтоги — Автозавод5Перегрузка операторов в ООП
Сложение объектовВычитание объектовКонкатенация объектовСравнение объектов (<, >)Повторение — Математика кошелька8Инкапсуляция
Соглашения об именованииЗамыкания для приватностиДоступ через замыканияТаблицы только для чтенияЛогика валидацииИтоги — Защищенное хранилище11Паттерны проектирования (Lite)
Фабричные функцииТаблица SingletonПаттерн IteratorObserver (Слушатель)Итоги — Logger Factory3Состояние и поведение объектов
Переменные экземпляраГеттерыСеттерыВычисляемые свойстваФорматирование строкПроверка на равенствоПовторение — Оценка студента6Основы наследования
Настройка наследованияНаследование методовРасширение конструктораДобавление дочерних методовОбщее и уникальноеИтоги: Иерархия фигурПотренируйтесь самостоятельно: Онлайн-компилятор Lua