Переменные экземпляра
Часть раздела Object Oriented Programming путешествия по Lua на Coddy — урок 12 из 70.
Добро пожаловать в новую главу, посвященную тому, как объекты управляют своими внутренними данными. Вы научились создавать классы и экземпляры — теперь пришло время изучить, как каждый экземпляр хранит и управляет своим собственным уникальным состоянием.
Переменные экземпляра — это поля, которые принадлежат исключительно одному объекту. Когда вы присваиваете значение self.something внутри метода или конструктора, эти данные живут только в этом конкретном экземпляре. Другие объекты того же класса имеют свои собственные отдельные копии.
Это становится особенно важным при хранении сложных данных, таких как таблицы. Рассмотрим систему инвентаря, где каждый персонаж носит разные предметы:
local Inventory = {}
Inventory.__index = Inventory
function Inventory:new()
local instance = {}
setmetatable(instance, self)
instance.items = {} -- Каждый экземпляр получает свою собственную пустую таблицу
return instance
end
function Inventory:addItem(item)
table.insert(self.items, item)
endКлючевая строка — instance.items = {}. Создавая новую пустую таблицу внутри конструктора, каждый объект инвентаря получает свой собственный отдельный список. Если бы вы вместо этого определили items в самой таблице класса, все экземпляры случайно использовали бы одну и ту же таблицу — добавление меча одному персонажу дало бы его всем!
local bag1 = Inventory:new()
local bag2 = Inventory:new()
bag1:addItem("Sword")
bag2:addItem("Potion")
print(#bag1.items) -- Вывод: 1
print(#bag2.items) -- Вывод: 1Каждый инвентарь хранит свою собственную коллекцию. Этот паттерн применяется в тех случаях, когда экземпляру необходимо отслеживать данные, которые изменяются независимо: списки, счетчики или любое изменяемое состояние, уникальное для этого объекта.
Задание
ЛегкоДавайте создадим систему ShoppingCart, в которой каждая корзина хранит свой собственный отдельный список товаров. Это продемонстрирует, как переменные экземпляра изолируют данные между объектами — добавление товаров в одну корзину не повлияет на другую!
Вы организуете свой код в двух файлах:
ShoppingCart.lua: Определите классShoppingCart, где каждый экземпляр имеет свою собственную таблицуproducts. Включите:- Конструктор
:new(), который инициализирует пустой список товаров для каждой корзины - Метод
:addProduct(productName), который добавляет товар в список этой корзины - Метод
:listProducts(), который выводит каждый товар на отдельной строке
- Конструктор
main.lua: Подключите модуль ShoppingCart и создайте две отдельные корзины. Добавьте разные товары в каждую корзину, чтобы доказать, что они поддерживают независимые списки.
Вы получите четыре входных значения:
- Первый товар для добавления в корзину A
- Второй товар для добавления в корзину A
- Первый товар для добавления в корзину B
- Второй товар для добавления в корзину B
В вашем основном файле:
- Создайте две корзины для покупок:
cartAиcartB - Добавьте первые два товара в
cartA - Добавьте последние два товара в
cartB - Выведите
Cart A:, а затем перечислите товары корзины A - Выведите
Cart B:, а затем перечислите товары корзины B
Например, если на вход подаются Apple, Bread, Milk и Eggs, вывод должен быть следующим:
Cart A:
Apple
Bread
Cart B:
Milk
EggsПомните: ключом является инициализация products как новой пустой таблицы внутри конструктора. Это гарантирует, что каждый экземпляр корзины получит свой собственный отдельный список, а не будет использовать одну общую таблицу для всех корзин.
Шпаргалка
Переменные экземпляра — это поля, которые принадлежат исключительно одному объекту. Когда вы присваиваете значение self.something внутри метода или конструктора, эти данные живут только в этом конкретном экземпляре.
Чтобы создать переменные экземпляра, инициализируйте их внутри конструктора, используя новую таблицу для каждого экземпляра:
local Inventory = {}
Inventory.__index = Inventory
function Inventory:new()
local instance = {}
setmetatable(instance, self)
instance.items = {} -- Each instance gets its own empty table
return instance
end
function Inventory:addItem(item)
table.insert(self.items, item)
endКлючевым моментом является instance.items = {} — создание новой пустой таблицы внутри конструктора гарантирует, что каждый объект получит свои собственные отдельные данные. Если бы вы определили items в самой таблице класса, все экземпляры использовали бы одну и ту же таблицу.
local bag1 = Inventory:new()
local bag2 = Inventory:new()
bag1:addItem("Sword")
bag2:addItem("Potion")
print(#bag1.items) -- Output: 1
print(#bag2.items) -- Output: 1Каждый экземпляр поддерживает свою собственную коллекцию независимо. Этот паттерн применим к любому изменяемому состоянию, уникальному для объекта — спискам, счетчикам или другим данным, которые меняются для каждого экземпляра.
Попробуйте сами
-- Подключить модуль ShoppingCart
local ShoppingCart = require('ShoppingCart')
-- Прочитать входные данные
local product1 = io.read()
local product2 = io.read()
local product3 = io.read()
local product4 = io.read()
-- TODO: Создать две корзины для покупок: cartA и cartB
-- TODO: Добавить product1 и product2 в cartA
-- TODO: Добавить product3 и product4 в cartB
-- TODO: Вывести "Cart A:", затем вывести список товаров cartA
-- TODO: Вывести "Cart B:", затем вывести список товаров cartB
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Object Oriented Programming
1Концепция 'self'
Таблицы с функциямиЯвный 'self'Синтаксис с двоеточиемТочка против двоеточияИтоги — Движущаяся точка4Проект: Цифровой банк
Настройка проектаМетод пополнения7Полиморфизм и переопределение
Переопределение методовВызов родительских методовУтиная типизацияОбщий интерфейсПроверка типаИтоги — Роли сотрудников2Паттерн прототипа класса
Концепция прототипаСвязывание через __indexКонструктор :new()Инициализация атрибутовНезависимые экземплярыИтоги — Автозавод5Перегрузка операторов в ООП
Сложение объектовВычитание объектовКонкатенация объектовСравнение объектов (<, >)Повторение — Математика кошелька8Инкапсуляция
Соглашения об именованииЗамыкания для приватностиДоступ через замыканияТаблицы только для чтенияЛогика валидацииИтоги — Защищенное хранилище11Паттерны проектирования (Lite)
Фабричные функцииТаблица SingletonПаттерн IteratorObserver (Слушатель)Итоги — Logger Factory3Состояние и поведение объектов
Переменные экземпляраГеттерыСеттерыВычисляемые свойстваФорматирование строкПроверка на равенствоПовторение — Оценка студента6Основы наследования
Настройка наследованияНаследование методовРасширение конструктораДобавление дочерних методовОбщее и уникальноеИтоги: Иерархия фигур