Menu
Coddy logo textTech

Связывание через __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. Экземпляр может иметь свои собственные уникальные данные, в то время как общее поведение находится в прототипе. В следующем уроке вы узнаете, как обернуть этот паттерн в чистую функцию-конструктор.

challenge icon

Задание

Легко

Теперь давайте применим метаметод __index на практике! Вы создадите прототип и свяжете с ним экземпляр, что позволит экземпляру получать доступ к свойствам и методам, которых у него нет напрямую.

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

  • Robot.lua: Определите таблицу-прототип Robot с:
    • Полем model, установленным в "RX-100"
    • Полем batteryLevel, установленным в 100
    • Методом :status(), который выводит текущее состояние робота
    Верните таблицу в конце файла.
  • main.lua: Подключите модуль Robot, затем создайте пустую таблицу экземпляра и свяжите ее с прототипом Robot, используя setmetatable с __index. Это позволит вашему пустому экземпляру получать доступ ко всему из прототипа.

Вы получите на вход одно значение: имя, которое нужно присвоить вашему экземпляру робота.

После связывания экземпляра с прототипом:

  1. Присвойте полученное имя полю name вашего экземпляра (оно станет уникальным для этого экземпляра)
  2. Вызовите метод :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() вашего экземпляра
quiz iconПроверьте себя

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

Все уроки раздела Object Oriented Programming