Связывание через __index
Часть раздела Object Oriented Programming путешествия по Lua на Coddy — урок 7 из 70.
У вас есть таблица-прототип со свойствами и методами, но как заставить новый объект действительно использовать её? Ответ кроется в метаметоде __index.
Когда вы обращаетесь к ключу, которого не существует в таблице, Lua обычно возвращает nil. Однако, если у таблицы есть метатаблица с полем __index, Lua вместо этого заглянет туда. Установив __index так, чтобы он указывал на ваш прототип, любые отсутствующие ключи будут автоматически найдены в прототипе:
local Dog = {
species = "Canis familiaris",
legs = 4
}
function Dog:bark()
print("Woof!")
end
-- Создаем пустой экземпляр
local myDog = {}
-- Связываем его с прототипом
setmetatable(myDog, { __index = Dog })
print(myDog.species) -- Вывод: Canis familiaris
myDog:bark() -- Вывод: Woof!Таблица myDog совершенно пуста, тем не менее обращение к myDog.species работает. Когда Lua не находит species в myDog, она проверяет __index метатаблицы и находит его в Dog.
Это основа объектно-ориентированного программирования в Lua. Экземпляр может иметь свои собственные уникальные данные, в то время как общее поведение находится в прототипе. В следующем уроке вы узнаете, как обернуть этот паттерн в чистую функцию-конструктор.
Задание
ЛегкоТеперь давайте применим метаметод __index на практике! Вы создадите прототип и свяжете с ним экземпляр, что позволит экземпляру получать доступ к свойствам и методам, которых у него нет напрямую.
Вы организуете свой код в двух файлах:
Robot.lua: Определите таблицу-прототипRobotс:- Полем
model, установленным в"RX-100" - Полем
batteryLevel, установленным в100 - Методом
:status(), который выводит текущее состояние робота
- Полем
main.lua: Подключите модуль Robot, затем создайте пустую таблицу экземпляра и свяжите ее с прототипом Robot, используяsetmetatableс__index. Это позволит вашему пустому экземпляру получать доступ ко всему из прототипа.
Вы получите на вход одно значение: имя, которое нужно присвоить вашему экземпляру робота.
После связывания экземпляра с прототипом:
- Присвойте полученное имя полю
nameвашего экземпляра (оно станет уникальным для этого экземпляра) - Вызовите метод
:status()у вашего экземпляра
Ваш метод :status() должен выводить данные именно в таком формате:
[name] ([model]) - Battery: [batteryLevel]%Например, если на вход подано Sparky, вывод должен быть следующим:
Sparky (RX-100) - Battery: 100%Обратите внимание, что у экземпляра есть собственное имя name, но он обращается к model и batteryLevel из прототипа через связь __index.
Шпаргалка
Метаметод __index позволяет объектам наследовать свойства и методы из таблицы-прототипа.
Когда вы обращаетесь к ключу, которого нет в таблице, Lua проверяет, есть ли у таблицы метатаблица с полем __index. Если оно есть, Lua ищет ключ в таблице, указанной в __index.
Чтобы связать экземпляр с прототипом:
local Prototype = {
property = "value"
}
function Prototype:method()
print("Method called")
end
-- Создание экземпляра
local instance = {}
-- Связывание с прототипом с помощью __index
setmetatable(instance, { __index = Prototype })
-- Доступ к свойствам и методам прототипа
print(instance.property) -- Вывод: value
instance:method() -- Вывод: Method calledТаблица экземпляра может быть пустой, но при этом она может обращаться ко всем свойствам и методам прототипа. Экземпляр также может иметь свои собственные уникальные свойства, которые переопределяют или расширяют прототип.
Этот паттерн является основой объектно-ориентированного программирования в Lua, где экземпляры разделяют поведение прототипа, сохраняя при этом свои собственные уникальные данные.
Попробуйте сами
-- Прочитать ввод
local name = io.read()
-- Подключить модуль Robot
local Robot = require('Robot')
-- TODO: Создать пустую таблицу экземпляра
-- TODO: Связать экземпляр с прототипом Robot, используя setmetatable с __index
-- TODO: Присвоить введенное имя полю name вашего экземпляра
-- TODO: Вызвать метод :status() вашего экземпляра
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Object Oriented Programming
1Концепция 'self'
Таблицы с функциямиЯвный 'self'Синтаксис с двоеточиемТочка против двоеточияИтоги — Движущаяся точка4Проект: Цифровой банк
Настройка проектаМетод пополнения7Полиморфизм и переопределение
Переопределение методовВызов родительских методовУтиная типизацияОбщий интерфейсПроверка типаИтоги — Роли сотрудников2Паттерн прототипа класса
Концепция прототипаСвязывание через __indexКонструктор :new()Инициализация атрибутовНезависимые экземплярыИтоги — Автозавод5Перегрузка операторов в ООП
Сложение объектовВычитание объектовКонкатенация объектовСравнение объектов (<, >)Повторение — Математика кошелька8Инкапсуляция
Соглашения об именованииЗамыкания для приватностиДоступ через замыканияТаблицы только для чтенияЛогика валидацииИтоги — Защищенное хранилище11Паттерны проектирования (Lite)
Фабричные функцииТаблица SingletonПаттерн IteratorObserver (Слушатель)Итоги — Logger Factory3Состояние и поведение объектов
Переменные экземпляраГеттерыСеттерыВычисляемые свойстваФорматирование строкПроверка на равенствоПовторение — Оценка студента6Основы наследования
Настройка наследованияНаследование методовРасширение конструктораДобавление дочерних методовОбщее и уникальноеИтоги: Иерархия фигур