Объектно-ориентированное программирование в Lua
Если ООП для вас новое, сначала пройдите материалы без привязки к синтаксису: парадигмы и уровни абстракции, затем ООП — о разделе — зачем объекты, введение, абстракция, инкапсуляция, наследование, полиморфизм.
Ниже — ООП через таблицы и метатаблицы в Lua.
Теория и модель Lua
В Lua нет встроенного ключевого слова class. ООП строится на таблицах и метатаблицах (прототипная модель, близкая к JavaScript).
Метатаблица — вторая таблица, привязанная к первой через setmetatable(obj, meta). Она задаёт поведение по умолчанию для обычной таблицы:
__index— куда смотреть, если ключа нет в самой таблице (типичное наследование методов класса).__newindex— что делать при записи в отсутствующий ключ (контроль присваивания).__call— что происходит при вызове таблицы как функции (Class()вместоClass:new()).__tostring— строковое представление дляprint.
Без метатаблицы таблица — просто набор полей. С метатаблицей та же таблица ведёт себя как объект с прототипом.
| Понятие ООП | Как выражено в Lua / Luau | Аналог в Java |
|---|---|---|
| Объект | таблица с полями и функциями | экземпляр класса |
| Класс (шаблон) | таблица-прототип + __index | class |
| Создание экземпляра | Class:new() / setmetatable({}, {__index = Class}) | new Class() |
| Конструктор | функция new / init в классе | ClassName(...) |
| Метод | поле-функция; вызов obj:method() | метод экземпляра |
self | первый аргумент метода (неявно через :) | this |
| Инкапсуляция | local upvalue, замыкания, соглашение _private | private |
| Наследование | цепочка метатаблиц __index | extends |
| Полиморфизм | duck typing: есть метод — объект подходит | интерфейсы + override |
| Абстракция | таблица-контракт без экземпляра; модуль API | interface, abstract class |
| Статика | поля на таблице класса, не на экземпляре | static |
| Метапрограммирование | __index, __newindex, __call | reflection (ограниченно) |
| Библиотеки ООП | MiddleClass, classic, Luau export type | встроенная модель |
В Lua нет ключевого слова class. Объект — таблица с полями и функциями; метод — поле-функция, а : подставляет self. Наследование (в том числе множественное) и сокрытие полей строятся цепочкой метатаблиц и метаметодов __index / __newindex, без отдельного синтаксиса языка. Подход ближе к JavaScript (прототипы), чем к Java/C#, и требует явной настройки.
Определения — раздел 4-08-oop. Синтаксис Lua — о разделе Lua и Luau. Для сравнения с номинальной моделью — ООП в Java.
Кратко для новичка:
- Таблица
{}— единственная структура данных; ею же моделируют объекты и классы. - Класс (прототип) — таблица с методами;
Class.__index = Class. - Экземпляр — новая таблица +
setmetatable({}, Class)илиClass:new(). - Метод —
obj:method()эквивалентенobj.method(obj).
Создание экземпляра и присваивание переменным
Таблица как объект
local point = { x = 10, y = 20 }
local alias = point -- та же ссылка на таблицу
alias.x = 99 -- изменится и point.x
local other = { x = 10, y = 20 } -- другая таблица в памяти
Разбор:
- В Lua все объекты (таблицы, функции, userdata) — ссылочные: присваивание копирует ссылку, не содержимое.
- Для копии таблицы — ручной перебор, функция
cloneв библиотеке или сериализация; аналогаcloneв Java нет в стандартной библиотеке Lua 5.x.
Паттерн Class:new() — конструктор
local Person = {}
Person.__index = Person
function Person:new(name, age)
local obj = setmetatable({}, self) -- self == Person
obj.name = name
obj.age = age or 0
return obj
end
local p = Person:new("Алексей", 30)
local q = p -- та же ссылка
local r = Person:new("Борис")
Разбор:
setmetatable({}, self)создаёт пустую таблицу-экземпляр; метатаблицаself(класс) с полем__indexуказывает, где искать методы (Person), если их нет в экземпляре.Person:new(...)— идиома: двоеточие передаётPersonкак первый аргументself.pиq— один объект;r— другой экземпляр с собственными полями.
Отличие . и :
-- Эквивалентны:
person.speak(person, "привет")
person:speak("привет")
Разбор:
- Точка не подставляет
self— при вызове метода через.нужно передать объект вручную. - Ошибка
attempt to index a nil valueчасто означает забытый:или отсутствующийself.
Видимость и инкапсуляция (нет private)
В Lua нет модификаторов доступа на уровне языка. Практики:
| Подход | Механизм | Надёжность |
|---|---|---|
| Соглашение | поле _balance — не трогать снаружи | низкая |
| Локальные upvalue | local balance внутри фабрики new | высокая |
| Модуль | local функции в module; наружу — API | высокая |
Luau --!strict | export type без лишних полей | проверка типов |
local function newAccount(owner)
local balance = 0 -- недоступен снаружи
local account = {}
function account:deposit(amount)
if amount > 0 then balance = balance + amount end
end
function account:getBalance()
return balance
end
account.owner = owner
return account
end
Разбор:
balanceживёт в замыкании — снаружи нет ключа для прямой записи.- Это ближе к Java
private float balance, чем к префиксу_.
Четыре столпа ООП в Lua
Интерактивные схемы — общие принципы (псевдокод). Подробнее: абстракция, инкапсуляция, наследование, полиморфизм.
Play ITЗагрузка интерактивного демо…
Play ITЗагрузка интерактивного демо…
Play ITЗагрузка интерактивного демо…
Play ITЗагрузка интерактивного демо…
Абстракция
Контракт задаётся таблицей с сигнатурами или модулем без конструирования абстрактного экземпляра:
-- Интерфейс — документация + проверка в Luau
export type Notifier = {
send: (self: Notifier, message: string) -> (),
}
local function createConsoleNotifier(): Notifier
return {
send = function(_, message)
print("[LOG] " .. message)
end,
}
end
Разбор:
- В чистом Lua контракт — соглашение; в Luau
export typeдаёт статическую проверку формы таблицы. - Аналог Java
interface— таблица с обязательными функциями; абстрактный класс — прототип с методами, вызывающимиerror("abstract")в базовой реализации.
Инкапсуляция
Скрытие состояния через замыкания и контролируемые методы (раздел Инкапсуляция ниже и embed lua-515-171-014).
Наследование
Цепочка __index: дочерний прототип ссылается на родителя; методы переопределяются полями в дочерней таблице.
Полиморфизм
Любая таблица с методом draw подходит для function render(shape) shape:draw() end — утиная типизация без implements.
Объектно-ориентированное программирование в Lua
Интерактивная схема — класс и объект (псевдокод, подходит для любого ООП-языка). Полный разбор принципов: ООП в разделе "Код и разработка".
КЛАСС Кот
поля: имя, возраст
метод мяукнуть()
КОНЕЦ
объект barsik := новый Кот(имя="Барсик", возраст=3)
barsik.мяукнуть()
Разбор:
- Псевдокод или схема: показывает идею без привязки к синтаксису Lua.
- В фрагменте 6 строк(и); читайте сверху вниз как последовательность шагов.
Play ITЗагрузка интерактивного демо…
Классы, объекты и элементы классов (свойства и методы)
Объектно-ориентированное программирование (ООП) — это парадигма программирования, которая организует код вокруг объектов, объединяющих данные и поведение. В языке Lua отсутствует встроенная поддержка классов как таковых, однако механизм таблиц и метатаблиц позволяет реализовать полноценную систему ООП, полностью соответствующую принципам объектной ориентации.
Класс — это шаблон или blueprint, определяющий структуру данных и набор функций, доступных для создания объектов. В Lua класс представляет собой обычную таблицу, содержащую поля для хранения свойств и методов. Таблица выступает в роли пространства имен, где хранятся определения всех элементов класса.
Объект — это конкретный экземпляр класса, созданный на основе шаблона. Объект обладает собственным состоянием, которое определяется значениями его свойств, и способностью выполнять действия через свои методы. Каждый объект в Lua является отдельной таблицой, наследующей структуру от родительского класса.
Свойство (поле) — это элемент данных, хранящийся внутри объекта или класса. Свойства описывают состояние объекта и могут иметь различные типы данных — числа, строки, таблицы, функции или другие объекты. В контексте ООП свойства часто называют атрибутами или членами класса.
Метод — это функция, связанная с объектом или классом, которая выполняет определенные действия над данными объекта. Методы определяют поведение системы и позволяют манипулировать состоянием объекта или взаимодействовать с другими частями программы. В Lua методы представляют собой функции, записанные внутри таблицы класса.
В языке Lua существует строгое различие между обращением к свойству и вызовом метода. Это различие реализуется через использование разных разделителей.
При обращении к свойству используется символ точки (.). Синтаксис выглядит следующим образом: object.name или class.property. Точка указывает интерпретатору Lua на необходимость получить значение конкретного поля из таблицы. Значение может быть любым типом данных, включая функцию, но при таком обращении функция не получает автоматического доступа к таблице-объекту.
При вызове метода используется символ двоеточия (:). Синтаксис выглядит следующим образом: object:methodName(). Двоеточие выполняет двойную функцию. Во-первых, оно вызывает функцию, записанную в таблице. Во-вторых, оно автоматически передает сам объект в качестве первого аргумента функции под именем self. Этот механизм позволяет методу работать с данными именно того объекта, который был указан перед двоеточием.
Различие между этими операторами критически важно для правильной работы кода. Использование точки вместо двоеточия при вызове метода приведет к ошибке, так как функция ожидает получить объект как первый параметр, но получит только ссылки на себя без контекста. Использование двоеточия при чтении свойства приведет к попытке вызвать функцию, что также вызовет ошибку выполнения.
Код ITЗагрузка примера кода…
Разбор:
functionобъявляет функцию;endзакрывает тело. Имя послеfunctionстановится ссылкой на вызываемый объект.- Синтаксис
obj:method()передаётobjпервым аргументом (self) — удобный стиль для методов таблиц. setmetatableсвязывает таблицу с метатаблицей; поля__…задают перехват операций (индекс, арифметика, вызов).- Фигурные скобки
{}создают таблицу: можно задать поляkey = valueи/или элементы-массив с индексами с 1. - Ключевые вызовы в фрагменте:
print(),setmetatable(). - Комментарии после
--поясняют ожидаемый результат; при запуске интерпретатор их игнорирует. - При переносе в свой проект сохраните порядок шагов и проверьте результат через
printили отладчик.
В приведенном примере таблица Person выступает в роли класса. Поле species является свойством класса. Функция speak является методом. Объект person1 создается как новая таблица с уникальными свойствами name и age. Механизм setmetatable с полем __index связывает объект с классом, позволяя искать недостающие поля и методы в таблице родителя.
Для свойства используют точку: person1.name — читается поле name в самой таблице (или через __index, если ключа нет).
Для метода — двоеточие: person1:speak() эквивалентно person1.speak(person1). Интерпретатор ищет speak в объекте и в цепочке __index, затем вызывает функцию, передавая person1 как первый аргумент self.
function Person:greet()
-- self ссылается на объект person1
print(self.name .. " говорит: " .. self.age .. " лет")
end
person1:greet() -- Выведет: Алексей говорит: 30 лет
Разбор:
functionобъявляет функцию;endзакрывает тело. Имя послеfunctionстановится ссылкой на вызываемый объект.- Синтаксис
obj:method()передаётobjпервым аргументом (self) — удобный стиль для методов таблиц. - Оператор
..склеивает строки (и автоматически приводит числа к строке при конкатенации). - Ключевые вызовы в фрагменте:
print(). - Комментарии после
--поясняют ожидаемый результат; при запуске интерпретатор их игнорирует. - При переносе в свой проект сохраните порядок шагов и проверьте результат через
printили отладчик.
Такой подход обеспечивает гибкость и мощь объектной модели Lua. Объекты могут иметь собственные уникальные свойства, при этом разделяя общую логику поведения с другими экземплярами того же класса. Система метатаблиц скрывает сложность реализации наследования и полиморфизма, предоставляя разработчику удобный синтаксический сахар.
Создание класса и экземпляра
Процесс создания класса в Lua начинается с объявления таблицы, которая будет служить шаблоном для будущих объектов. Эта таблица содержит определения всех полей и методов, которые будут доступны экземплярам. Важно понимать, что сама таблица не является классом в традиционном смысле, пока не будет настроена система метатаблиц для поддержки наследования.
Для создания базового класса достаточно объявить пустую таблицу и добавить в нее функции и переменные.
local MyClass = {}
MyClass.value = 0
function MyClass:init(val)
self.value = val
end
function MyClass:getValue()
return self.value
end
Разбор:
functionобъявляет функцию;endзакрывает тело. Имя послеfunctionстановится ссылкой на вызываемый объект.- Синтаксис
obj:method()передаётobjпервым аргументом (self) — удобный стиль для методов таблиц. returnзавершает функцию и может вернуть несколько значений через запятую.- При переносе в свой проект сохраните порядок шагов и проверьте результат через
printили отладчик.
Таблица MyClass теперь содержит статическое поле value и два метода init и getValue. Однако, чтобы использовать этот шаблон для создания объектов, необходимо создать механизм, который позволит новым таблицам наследовать эти члены.
Создание экземпляра (объекта) происходит путем создания новой таблицы и назначения ей метатаблицы, указывающей на родительский класс. Ключевым механизмом здесь является поле __index в метатаблице. Когда программа пытается обратиться к полю объекта, которого там нет, Lua автоматически смотрит в таблицу, указанную в __index, и продолжает поиск там.
-- Создание экземпляра
local obj1 = setmetatable({}, {__index = MyClass})
-- Инициализация объекта
obj1:init(42)
-- Вызов метода
print(obj1:getValue()) -- Выведет: 42
Разбор:
setmetatableсвязывает таблицу с метатаблицей; поля__…задают перехват операций (индекс, арифметика, вызов).- Фигурные скобки
{}создают таблицу: можно задать поляkey = valueи/или элементы-массив с индексами с 1. - Ключевые вызовы в фрагменте:
print(),setmetatable(). - Комментарии после
--поясняют ожидаемый результат; при запуске интерпретатор их игнорирует. - При переносе в свой проект сохраните порядок шагов и проверьте результат через
printили отладчик.
В данном коде создаётся новая пустая таблица obj1. Ей присваивается метатаблица с полем __index, равным MyClass. При вызове obj1:init(42) интерпретатор ищет метод init в obj1. Не находя его там, он переходит к MyClass и находит функцию. Функция вызывается, и self внутри неё ссылается на obj1.
Конструктор в Lua — это специальная функция, предназначенная для первичной настройки нового объекта. В отличие от строго типизированных языков, где конструктор имеет фиксированное имя, в Lua роль конструктора может играть любая функция, обычно называемая new или create, либо метод init, вызываемый сразу после создания объекта.
Реализация конструктора через метод new является наиболее распространенной практикой. Такой подход инкапсулирует процесс создания и инициализации объекта в одном месте.
Код ITЗагрузка примера кода…
Разбор:
functionобъявляет функцию;endзакрывает тело. Имя послеfunctionстановится ссылкой на вызываемый объект.- Синтаксис
obj:method()передаётobjпервым аргументом (self) — удобный стиль для методов таблиц. - Логика записывается словами
and/or/not, а не символами&&/||/!как в C-подобных языках. returnзавершает функцию и может вернуть несколько значений через запятую.setmetatableсвязывает таблицу с метатаблицей; поля__…задают перехват операций (индекс, арифметика, вызов).- Фигурные скобки
{}создают таблицу: можно задать поляkey = valueи/или элементы-массив с индексами с 1. - Ключевые вызовы в фрагменте:
print(),setmetatable(). - Комментарии после
--поясняют ожидаемый результат; при запуске интерпретатор их игнорирует. - При переносе в свой проект сохраните порядок шагов и проверьте результат через
printили отладчик.
В методе new создается новая таблица instance, которой назначается метатаблица со ссылкой на self (то есть на таблицу Rectangle). Затем происходит инициализация полей width и height. Если аргументы не переданы, используются значения по умолчанию. Функция возвращает готовый объект.
Альтернативный подход использует метод init как конструктор, который вызывается вручную после создания пустого объекта.
Код ITЗагрузка примера кода…
Разбор:
functionобъявляет функцию;endзакрывает тело. Имя послеfunctionстановится ссылкой на вызываемый объект.- Синтаксис
obj:method()передаётobjпервым аргументом (self) — удобный стиль для методов таблиц. returnзавершает функцию и может вернуть несколько значений через запятую.setmetatableсвязывает таблицу с метатаблицей; поля__…задают перехват операций (индекс, арифметика, вызов).- Фигурные скобки
{}создают таблицу: можно задать поляkey = valueи/или элементы-массив с индексами с 1. - Ключевые вызовы в фрагменте:
setmetatable(). - Комментарии после
--поясняют ожидаемый результат; при запуске интерпретатор их игнорирует. - При переносе в свой проект сохраните порядок шагов и проверьте результат через
printили отладчик.
Этот подход менее удобен, так как требует двух шагов для создания объекта, но иногда используется для разделения процесса выделения памяти и логической инициализации.
Конструкторы также могут принимать сложные параметры, такие как таблицы конфигурации, что позволяет создавать гибкие объекты.
Код ITЗагрузка примера кода…
Разбор:
functionобъявляет функцию;endзакрывает тело. Имя послеfunctionстановится ссылкой на вызываемый объект.- Синтаксис
obj:method()передаётobjпервым аргументом (self) — удобный стиль для методов таблиц. ipairsобходит последовательную часть таблицы (индексы 1,2,3…);pairs— все пары ключ-значение.- Цикл
forповторяет тело: в числовой форме перебирает диапазон, в generic — элементы через итератор. - Логика записывается словами
and/or/not, а не символами&&/||/!как в C-подобных языках. returnзавершает функцию и может вернуть несколько значений через запятую.setmetatableсвязывает таблицу с метатаблицей; поля__…задают перехват операций (индекс, арифметика, вызов).- Фигурные скобки
{}создают таблицу: можно задать поляkey = valueи/или элементы-массив с индексами с 1. - Ключевые вызовы в фрагменте —
pairs(),print(),setmetatable().
При создании экземпляра важно помнить, что каждая новая таблица создает новую область памяти. Изменение свойств одного объекта не влияет на другие объекты того же класса, если они не используют общие ссылки на одну и ту же таблицу.
local SharedData = {count = 0}
local ObjA = setmetatable({data = SharedData}, {__index = MyParent})
local ObjB = setmetatable({data = SharedData}, {__index = MyParent})
ObjA.Data.count = 10
print(ObjB.Data.count) -- 10, так как обе таблицы ссылаются на один объект SharedData
Разбор:
setmetatableсвязывает таблицу с метатаблицей; поля__…задают перехват операций (индекс, арифметика, вызов).- Фигурные скобки
{}создают таблицу: можно задать поляkey = valueи/или элементы-массив с индексами с 1. - Ключевые вызовы в фрагменте:
print(),setmetatable(). - Комментарии после
--поясняют ожидаемый результат; при запуске интерпретатор их игнорирует. - При переносе в свой проект сохраните порядок шагов и проверьте результат через
printили отладчик.
Если требуется изоляция данных, каждый экземпляр должен получать свою собственную копию таблицы или значения.
function MyClass:new()
local instance = setmetatable({}, {__index = self})
instance.Data = {} -- Новая таблица для каждого экземпляра
return instance
end
Разбор:
functionобъявляет функцию;endзакрывает тело. Имя послеfunctionстановится ссылкой на вызываемый объект.- Синтаксис
obj:method()передаётobjпервым аргументом (self) — удобный стиль для методов таблиц. returnзавершает функцию и может вернуть несколько значений через запятую.setmetatableсвязывает таблицу с метатаблицей; поля__…задают перехват операций (индекс, арифметика, вызов).- Фигурные скобки
{}создают таблицу: можно задать поляkey = valueи/или элементы-массив с индексами с 1. - Ключевые вызовы в фрагменте:
setmetatable(). - Комментарии после
--поясняют ожидаемый результат; при запуске интерпретатор их игнорирует. - При переносе в свой проект сохраните порядок шагов и проверьте результат через
printили отладчик.
Система создания классов и объектов в Lua основана на динамической природе таблиц и метатаблиц. Это дает разработчику полный контроль над структурой объектов и позволяет адаптировать модель под любые нужды проекта.
Конструкторы
Конструктор — это специальный метод или функция, ответственная за создание и начальную настройку нового объекта. В языке Lua конструктор не является зарезервированным элементом языка, поэтому разработчик определяет его сигнатуру и поведение самостоятельно. Основная задача конструктора — подготовить объект к использованию, установив начальные значения свойств и выполнив необходимые проверки.
В Lua принято выделять два этапа работы конструктора: выделение структуры объекта и инициализация его состояния. Часто эти этапы объединяются в одну функцию, называемую new. Эта функция принимает параметры, необходимые для создания объекта, создает новую таблицу, настраивает её метатаблицу для наследования и возвращает готовый экземпляр.
Код ITЗагрузка примера кода…
Разбор:
functionобъявляет функцию;endзакрывает тело. Имя послеfunctionстановится ссылкой на вызываемый объект.- Синтаксис
obj:method()передаётobjпервым аргументом (self) — удобный стиль для методов таблиц. if … then … endвыбирает ветку по truthiness: ложными считаются толькоnilиfalse.- Логика записывается словами
and/or/not, а не символами&&/||/!как в C-подобных языках. returnзавершает функцию и может вернуть несколько значений через запятую.setmetatableсвязывает таблицу с метатаблицей; поля__…задают перехват операций (индекс, арифметика, вызов).- Вызовы из стандартных библиотек
table— готовые функции языка, не нужно писать их с нуля. type Имя = { … }в Luau объявляет именованный тип для статической проверки.- Фигурные скобки
{}создают таблицу: можно задать поляkey = valueи/или элементы-массив с индексами с 1.
В примере выше функция BankAccount:new выступает в роли конструктора. Она принимает имя владельца и начальную сумму баланса. Если баланс не указан, устанавливается значение по умолчанию 0. Также создается пустая таблица для истории транзакций. Возвращаемый объект уже полностью готов к работе.
Конструкторы могут содержать логику валидации входных данных. Это позволяет предотвратить создание объектов в недопустимом состоянии. См. Проверка и валидация.
Код ITЗагрузка примера кода…
Разбор:
functionобъявляет функцию;endзакрывает тело. Имя послеfunctionстановится ссылкой на вызываемый объект.- Синтаксис
obj:method()передаётobjпервым аргументом (self) — удобный стиль для методов таблиц. if … then … endвыбирает ветку по truthiness: ложными считаются толькоnilиfalse.- Логика записывается словами
and/or/not, а не символами&&/||/!как в C-подобных языках. returnзавершает функцию и может вернуть несколько значений через запятую.setmetatableсвязывает таблицу с метатаблицей; поля__…задают перехват операций (индекс, арифметика, вызов).- Вызовы из стандартных библиотек
string— готовые функции языка, не нужно писать их с нуля. - Фигурные скобки
{}создают таблицу: можно задать поляkey = valueи/или элементы-массив с индексами с 1. - Ключевые вызовы в фрагменте —
error(),setmetatable(),string.match().
При попытке создать пользователя с коротким именем или неправильным email возникнет ошибка, что предотвратит появление некорректных данных в системе.
Конструкторы могут также выполнять сложные операции, такие как подключение к базе данных, чтение файлов или сетевые запросы.
Код ITЗагрузка примера кода…
Разбор:
functionобъявляет функцию;endзакрывает тело. Имя послеfunctionстановится ссылкой на вызываемый объект.- Синтаксис
obj:method()передаётobjпервым аргументом (self) — удобный стиль для методов таблиц. - Оператор
..склеивает строки (и автоматически приводит числа к строке при конкатенации). returnзавершает функцию и может вернуть несколько значений через запятую.setmetatableсвязывает таблицу с метатаблицей; поля__…задают перехват операций (индекс, арифметика, вызов).- Фигурные скобки
{}создают таблицу: можно задать поляkey = valueи/или элементы-массив с индексами с 1. - Ключевые вызовы в фрагменте:
print(),setmetatable(). - Комментарии после
--поясняют ожидаемый результат; при запуске интерпретатор их игнорирует. - При переносе в свой проект сохраните порядок шагов и проверьте результат через
printили отладчик.
В этом случае конструктор автоматически инициирует соединение, избавляя разработчика от необходимости вызывать метод подключения отдельно.
Наследование конструкторов позволяет создавать специализированные версии базовых классов. Дочерний класс может вызывать конструктор родительского класса для получения базовой функциональности, а затем добавлять свои особенности.
Код ITЗагрузка примера кода…
Разбор:
functionобъявляет функцию;endзакрывает тело. Имя послеfunctionстановится ссылкой на вызываемый объект.- Синтаксис
obj:method()передаётobjпервым аргументом (self) — удобный стиль для методов таблиц. returnзавершает функцию и может вернуть несколько значений через запятую.setmetatableсвязывает таблицу с метатаблицей; поля__…задают перехват операций (индекс, арифметика, вызов).type Имя = { … }в Luau объявляет именованный тип для статической проверки.- Фигурные скобки
{}создают таблицу: можно задать поляkey = valueи/или элементы-массив с индексами с 1. - Ключевые вызовы в фрагменте:
print(),setmetatable(). - Комментарии после
--поясняют ожидаемый результат; при запуске интерпретатор их игнорирует. - При переносе в свой проект сохраните порядок шагов и проверьте результат через
printили отладчик.
Здесь Car:new создаёт экземпляр через конструктор родителя Vehicle.new(Vehicle, "Car"), затем вешает метатаблицу Car, чтобы методы вроде :refuel искались в цепочке Car → Vehicle.
Конструкторы могут использовать паттерн прототипного наследования, когда новый объект создается путем клонирования существующего.
Код ITЗагрузка примера кода…
Разбор:
functionобъявляет функцию;endзакрывает тело. Имя послеfunctionстановится ссылкой на вызываемый объект.- Синтаксис
obj:method()передаётobjпервым аргументом (self) — удобный стиль для методов таблиц. ipairsобходит последовательную часть таблицы (индексы 1,2,3…);pairs— все пары ключ-значение.- Цикл
forповторяет тело: в числовой форме перебирает диапазон, в generic — элементы через итератор. returnзавершает функцию и может вернуть несколько значений через запятую.setmetatableсвязывает таблицу с метатаблицей; поля__…задают перехват операций (индекс, арифметика, вызов).type Имя = { … }в Luau объявляет именованный тип для статической проверки.- Фигурные скобки
{}создают таблицу: можно задать поляkey = valueи/или элементы-массив с индексами с 1. - Ключевые вызовы в фрагменте —
pairs(),print(),setmetatable().
Такой подход полезен при создании множества похожих объектов с небольшими отличиями.
Важным аспектом работы конструкторов является управление памятью. Поскольку Lua использует сборщик мусора, объекты, созданные в конструкторе, автоматически освобождаются, когда перестают использоваться. Однако, если конструктор открывает внешние ресурсы (файлы, сокеты, соединения БД), их необходимо закрывать в деструкторе или специальном методе очистки.
Код ITЗагрузка примера кода…
Разбор:
functionобъявляет функцию;endзакрывает тело. Имя послеfunctionстановится ссылкой на вызываемый объект.- Синтаксис
obj:method()передаётobjпервым аргументом (self) — удобный стиль для методов таблиц. if … then … endвыбирает ветку по truthiness: ложными считаются толькоnilиfalse.- Логика записывается словами
and/or/not, а не символами&&/||/!как в C-подобных языках. returnзавершает функцию и может вернуть несколько значений через запятую.setmetatableсвязывает таблицу с метатаблицей; поля__…задают перехват операций (индекс, арифметика, вызов).io.openоткрывает файл; режим"r"/"w"/"a"задаёт чтение, перезапись или дозапись.- Фигурные скобки
{}создают таблицу: можно задать поляkey = valueи/или элементы-массив с индексами с 1. - Ключевые вызовы в фрагменте:
error(),io.open(),setmetatable().
Конструкторы в Lua предоставляют мощный инструмент для создания гибких и надежных объектов. Они позволяют инкапсулировать логику инициализации, обеспечивать целостность данных и упрощать работу с объектами для других частей программы.
Наследование
Интерактивная схема — наследование (псевдокод). Подробнее: Наследование.
Play ITЗагрузка интерактивного демо…
Наследование — это механизм объектно-ориентированного программирования, позволяющий одному классу (дочернему или производному) заимствовать свойства и методы другого класса (родительского или базового). В Lua реализация наследования основана на использовании метатаблиц и ключевого поля __index. Этот механизм обеспечивает динамическое разрешение членов класса, позволяя объектам получать доступ к элементам, определенным в родительской таблице.
Процесс наследования в Lua начинается с создания дочерней таблицы, которая должна ссылаться на родительскую таблицу через метатаблицу. При этом родительская таблица становится точкой поиска для всех отсутствующих полей и методов дочернего объекта.
Код ITЗагрузка примера кода…
Разбор:
functionобъявляет функцию;endзакрывает тело. Имя послеfunctionстановится ссылкой на вызываемый объект.- Синтаксис
obj:method()передаётobjпервым аргументом (self) — удобный стиль для методов таблиц. - Оператор
..склеивает строки (и автоматически приводит числа к строке при конкатенации). returnзавершает функцию и может вернуть несколько значений через запятую.setmetatableсвязывает таблицу с метатаблицей; поля__…задают перехват операций (индекс, арифметика, вызов).- Фигурные скобки
{}создают таблицу: можно задать поляkey = valueи/или элементы-массив с индексами с 1. - Ключевые вызовы в фрагменте:
print(),setmetatable(). - Комментарии после
--поясняют ожидаемый результат; при запуске интерпретатор их игнорирует. - При переносе в свой проект сохраните порядок шагов и проверьте результат через
printили отладчик.
В примере выше таблица Dog наследуется от Animal через setmetatable(Dog, {__index = Animal}). Метод speak переопределяется в классе Dog, что демонстрирует способность дочерних классов изменять поведение родителей. При этом свойство age остается доступным, так как оно определено в родительском классе.
Конструктор дочернего класса вызывает Animal.new(Animal, name), затем задаёт метатаблицу Dog, чтобы экземпляр видел переопределённые методы.
Наследование может быть многоступенчатым, когда класс наследуется от класса, который уже является потомком другого класса.
Код ITЗагрузка примера кода…
Разбор:
functionобъявляет функцию;endзакрывает тело. Имя послеfunctionстановится ссылкой на вызываемый объект.- Синтаксис
obj:method()передаётobjпервым аргументом (self) — удобный стиль для методов таблиц. - Оператор
..склеивает строки (и автоматически приводит числа к строке при конкатенации). returnзавершает функцию и может вернуть несколько значений через запятую.setmetatableсвязывает таблицу с метатаблицей; поля__…задают перехват операций (индекс, арифметика, вызов).- Фигурные скобки
{}создают таблицу: можно задать поляkey = valueи/или элементы-массив с индексами с 1. - Ключевые вызовы в фрагменте:
print(),setmetatable(). - Комментарии после
--поясняют ожидаемый результат; при запуске интерпретатор их игнорирует. - При переносе в свой проект сохраните порядок шагов и проверьте результат через
printили отладчик.
Цепочка наследования Cat -> Mammal -> Animal позволяет классу Cat использовать функциональность всех предков. Метод speak переопределяется на каждом уровне, обеспечивая специфичное поведение для каждого типа животного.
Динамическая природа Lua позволяет изменять поведение классов во время выполнения. Можно добавлять новые методы в родительский класс, и все наследники автоматически получат доступ к ним.
function Animal:move()
print(self.name .. " движется")
end
myDog:move() -- Рекс движется
myCat:move() -- Барсик движется
Разбор:
functionобъявляет функцию;endзакрывает тело. Имя послеfunctionстановится ссылкой на вызываемый объект.- Синтаксис
obj:method()передаётobjпервым аргументом (self) — удобный стиль для методов таблиц. - Оператор
..склеивает строки (и автоматически приводит числа к строке при конкатенации). - Ключевые вызовы в фрагменте:
print(). - Комментарии после
--поясняют ожидаемый результат; при запуске интерпретатор их игнорирует. - При переносе в свой проект сохраните порядок шагов и проверьте результат через
printили отладчик.
Полиморфизм в сочетании с наследованием позволяет писать универсальный код, работающий с объектами разных типов, но имеющими общий интерфейс.
function makeSound(animal)
animal:speak()
end
makeSound(myDog) -- Рекс лает: Гав!
makeSound(myCat) -- Барсик мяукает: Мяу!
makeSound(myDog) -- Рекс лает: Гав!
Разбор:
functionобъявляет функцию;endзакрывает тело. Имя послеfunctionстановится ссылкой на вызываемый объект.- Комментарии после
--поясняют ожидаемый результат; при запуске интерпретатор их игнорирует. - При переносе в свой проект сохраните порядок шагов и проверьте результат через
printили отладчик.
Функция makeSound принимает любой объект, у которого есть метод speak, и вызывает его. Благодаря наследованию и полиморфизму, она работает корректно для всех типов животных.
Наследование также поддерживает множественное наследование через составление метатаблиц, хотя стандартный подход в Lua предполагает одноуровневое наследование.
Код ITЗагрузка примера кода…
Разбор:
- Оператор
..склеивает строки (и автоматически приводит числа к строке при конкатенации). ipairsобходит последовательную часть таблицы (индексы 1,2,3…);pairs— все пары ключ-значение.- Цикл
forповторяет тело: в числовой форме перебирает диапазон, в generic — элементы через итератор. setmetatableсвязывает таблицу с метатаблицей; поля__…задают перехват операций (индекс, арифметика, вызов).- Фигурные скобки
{}создают таблицу: можно задать поляkey = valueи/или элементы-массив с индексами с 1. - Ключевые вызовы в фрагменте —
pairs(),print(),setmetatable(). - Комментарии после
--поясняют ожидаемый результат; при запуске интерпретатор их игнорирует. - При переносе в свой проект сохраните порядок шагов и проверьте результат через
printили отладчик.
Хотя Lua не поддерживает множественное наследование на уровне метатаблиц напрямую, разработчики могут комбинировать функциональность нескольких таблиц, вручную копируя методы или используя более сложные схемы композиции.
Наследование в Lua предоставляет гибкую и мощную систему для построения иерархий классов. Оно позволяет повторно использовать код, расширять функциональность и создавать абстрактные модели, отражающие реальные отношения между объектами.
Инкапсуляция
Интерактивная схема — инкапсуляция (псевдокод). Подробнее: Инкапсуляция.
Play ITЗагрузка интерактивного демо…
Инкапсуляция — это принцип объектно-ориентированного программирования, который ограничивает прямой доступ к некоторым компонентам объекта, скрывая внутреннюю реализацию и предоставляя контролируемый интерфейс для взаимодействия. В Lua инкапсуляция достигается за счет использования локальных переменных, замыканий и правил соглашения об именах, так как язык не имеет жестких модификаторов доступа вроде private или public.
Основная идея инкапсуляции заключается в том, что данные объекта должны быть защищены от прямого изменения извне. Вместо этого изменение должно происходить через специальные методы, которые выполняют проверку данных и поддерживают целостность состояния объекта.
В Lua принято считать, что переменные, начинающиеся с подчеркивания (_), являются приватными и не предназначены для прямого доступа извне. Это соглашение помогает разработчикам идентифицировать внутренние данные, которые должны быть скрыты.
Код ITЗагрузка примера кода…
Разбор:
functionобъявляет функцию;endзакрывает тело. Имя послеfunctionстановится ссылкой на вызываемый объект.- Синтаксис
obj:method()передаётobjпервым аргументом (self) — удобный стиль для методов таблиц. if … then … endвыбирает ветку по truthiness: ложными считаются толькоnilиfalse.- Логика записывается словами
and/or/not, а не символами&&/||/!как в C-подобных языках. returnзавершает функцию и может вернуть несколько значений через запятую.setmetatableсвязывает таблицу с метатаблицей; поля__…задают перехват операций (индекс, арифметика, вызов).- Фигурные скобки
{}создают таблицу: можно задать поляkey = valueи/или элементы-массив с индексами с 1. - Ключевые вызовы в фрагменте:
print(),setmetatable(). - Комментарии после
--поясняют ожидаемый результат; при запуске интерпретатор их игнорирует.
В этом примере данные счета (balance и owner) хранятся в локальной таблице privateData, которая недоступна извне. Доступ к ним осуществляется только через методы getBalance, setOwner, deposit и withdraw. Эти методы содержат логику проверки, например, запрет на снятие средств, превышающих баланс.
Использование замыканий позволяет создать истинную инкапсуляцию, так как переменные находятся в области видимости функции конструктора и не могут быть изменены напрямую.
Код ITЗагрузка примера кода…
Разбор:
functionобъявляет функцию;endзакрывает тело. Имя послеfunctionстановится ссылкой на вызываемый объект.- Синтаксис
obj:method()передаётobjпервым аргументом (self) — удобный стиль для методов таблиц. - Логика записывается словами
and/or/not, а не символами&&/||/!как в C-подобных языках. returnзавершает функцию и может вернуть несколько значений через запятую.setmetatableсвязывает таблицу с метатаблицей; поля__…задают перехват операций (индекс, арифметика, вызов).- Фигурные скобки
{}создают таблицу: можно задать поляkey = valueи/или элементы-массив с индексами с 1. - Ключевые вызовы в фрагменте:
print(),setmetatable(). - Комментарии после
--поясняют ожидаемый результат; при запуске интерпретатор их игнорирует. - При переносе в свой проект сохраните порядок шагов и проверьте результат через
printили отладчик.
Здесь переменная count находится в области видимости функции Counter:new. Даже если кто-то попытается изменить её напрямую, это не удастся, так как она не экспортируется в публичный интерфейс.
Инкапсуляция также позволяет внедрить логику валидации и аудита изменений.
Код ITЗагрузка примера кода…
Разбор:
functionобъявляет функцию;endзакрывает тело. Имя послеfunctionстановится ссылкой на вызываемый объект.- Синтаксис
obj:method()передаётobjпервым аргументом (self) — удобный стиль для методов таблиц. ipairsобходит последовательную часть таблицы (индексы 1,2,3…);pairs— все пары ключ-значение.- Цикл
forповторяет тело: в числовой форме перебирает диапазон, в generic — элементы через итератор. if … then … endвыбирает ветку по truthiness: ложными считаются толькоnilиfalse.- Логика записывается словами
and/or/not, а не символами&&/||/!как в C-подобных языках. returnзавершает функцию и может вернуть несколько значений через запятую.setmetatableсвязывает таблицу с метатаблицей; поля__…задают перехват операций (индекс, арифметика, вызов).- Вызовы из стандартных библиотек
table— готовые функции языка, не нужно писать их с нуля.
В этом примере метод addGrade проверяет допустимость оценки перед добавлением её в список. Метод getAverage вычисляет среднее значение, используя только легальные данные.
Инкапсуляция также полезна для управления состоянием объекта. Например, можно запретить изменение определенных полей после инициализации.
Код ITЗагрузка примера кода…
Разбор:
functionобъявляет функцию;endзакрывает тело. Имя послеfunctionстановится ссылкой на вызываемый объект.- Синтаксис
obj:method()передаётobjпервым аргументом (self) — удобный стиль для методов таблиц. if … then … endвыбирает ветку по truthiness: ложными считаются толькоnilиfalse.returnзавершает функцию и может вернуть несколько значений через запятую.setmetatableсвязывает таблицу с метатаблицей; поля__…задают перехват операций (индекс, арифметика, вызов).- Фигурные скобки
{}создают таблицу: можно задать поляkey = valueи/или элементы-массив с индексами с 1. - Ключевые вызовы в фрагменте:
error(),setmetatable(). - Комментарии после
--поясняют ожидаемый результат; при запуске интерпретатор их игнорирует. - При переносе в свой проект сохраните порядок шагов и проверьте результат через
printили отладчик.
Инкапсуляция в Lua, хотя и не имеет жесткой защиты на уровне языка, эффективно реализуется через архитектурные решения и соглашения. Она обеспечивает безопасность данных, упрощает тестирование и делает код более понятным и поддерживаемым.
Полиморфизм
Интерактивная схема — полиморфизм (псевдокод). Подробнее: Полиморфизм.
Play ITЗагрузка интерактивного демо…
Полиморфизм — это способность объектов разных классов обрабатываться одинаковым образом через общий интерфейс, несмотря на различия во внутренней реализации. В языке Lua полиморфизм реализуется динамически благодаря наличию таблиц и метатаблиц, что позволяет функциям работать с любыми объектами, обладающими необходимыми методами.
В Lua полиморфизм не требует явного объявления интерфейсов или абстрактных классов. Достаточно, чтобы объекты имели методы с одинаковыми именами и сигнатурами. Интерпретатор Lua разрешает вызов этих методов в runtime, опираясь на фактическое наличие метода в объекте.
Код ITЗагрузка примера кода…
Разбор:
functionобъявляет функцию;endзакрывает тело. Имя послеfunctionстановится ссылкой на вызываемый объект.- Синтаксис
obj:method()передаётobjпервым аргументом (self) — удобный стиль для методов таблиц. - Оператор
..склеивает строки (и автоматически приводит числа к строке при конкатенации). ipairsобходит последовательную часть таблицы (индексы 1,2,3…);pairs— все пары ключ-значение.- Цикл
forповторяет тело: в числовой форме перебирает диапазон, в generic — элементы через итератор. returnзавершает функцию и может вернуть несколько значений через запятую.setmetatableсвязывает таблицу с метатаблицей; поля__…задают перехват операций (индекс, арифметика, вызов).- Фигурные скобки
{}создают таблицу: можно задать поляkey = valueи/или элементы-массив с индексами с 1. - Ключевые вызовы в фрагменте —
ipairs(),print(),setmetatable().
В этом примере массив shapes содержит объекты разных типов: Circle, Square и базовый Shape. Цикл перебирает все объекты и вызывает метод draw. Несмотря на разные реализации, каждый объект корректно выполняет свою версию метода. Это и есть проявление полиморфизма.
Рисуется круг радиусом 5
Рисуется квадрат со стороной 10
Рисуется фигура
Разбор:
- Псевдокод или схема: показывает идею без привязки к синтаксису Lua.
- В фрагменте 3 строк(и); читайте сверху вниз как последовательность шагов.
Полиморфизм позволяет писать универсальный код, который не зависит от конкретных типов объектов. Функция может принимать любой объект, имеющий нужный метод, и выполнять над ним действие.
function renderAll(objects)
for _, obj in ipairs(objects) do
if obj.draw then
obj:draw()
end
end
end
renderAll(shapes)
Разбор:
functionобъявляет функцию;endзакрывает тело. Имя послеfunctionстановится ссылкой на вызываемый объект.ipairsобходит последовательную часть таблицы (индексы 1,2,3…);pairs— все пары ключ-значение.- Цикл
forповторяет тело: в числовой форме перебирает диапазон, в generic — элементы через итератор. if … then … endвыбирает ветку по truthiness: ложными считаются толькоnilиfalse.- Ключевые вызовы в фрагменте:
ipairs(). - При переносе в свой проект сохраните порядок шагов и проверьте результат через
printили отладчик.
Функция renderAll проверяет наличие метода draw в каждом объекте и вызывает его. Если метод отсутствует, объект пропускается. Это делает код устойчивым к добавлению новых типов фигур без необходимости изменения функции рендеринга.
Динамический полиморфизм в Lua также позволяет заменять поведение объектов во время выполнения.
Код ITЗагрузка примера кода…
Разбор:
functionобъявляет функцию;endзакрывает тело. Имя послеfunctionстановится ссылкой на вызываемый объект.- Синтаксис
obj:method()передаётobjпервым аргументом (self) — удобный стиль для методов таблиц. - Оператор
..склеивает строки (и автоматически приводит числа к строке при конкатенации). returnзавершает функцию и может вернуть несколько значений через запятую.setmetatableсвязывает таблицу с метатаблицей; поля__…задают перехват операций (индекс, арифметика, вызов).- Фигурные скобки
{}создают таблицу: можно задать поляkey = valueи/или элементы-массив с индексами с 1. - Ключевые вызовы в фрагменте:
print(),setmetatable(). - Комментарии после
--поясняют ожидаемый результат; при запуске интерпретатор их игнорирует. - При переносе в свой проект сохраните порядок шагов и проверьте результат через
printили отладчик.
Метод action может быть изменен для любого экземпляра, что демонстрирует гибкость полиморфизма в Lua.
Полиморфизм также проявляется в перегрузке методов через проверку типов аргументов.
Код ITЗагрузка примера кода…
Разбор:
functionобъявляет функцию;endзакрывает тело. Имя послеfunctionстановится ссылкой на вызываемый объект.- Синтаксис
obj:method()передаётobjпервым аргументом (self) — удобный стиль для методов таблиц. - Оператор
..склеивает строки (и автоматически приводит числа к строке при конкатенации). if … then … endвыбирает ветку по truthiness: ложными считаются толькоnilиfalse.- Логика записывается словами
and/or/not, а не символами&&/||/!как в C-подобных языках. type(x)возвращает строку с именем типа значения (number,string,table,functionи т.д.).returnзавершает функцию и может вернуть несколько значений через запятую.setmetatableсвязывает таблицу с метатаблицей; поля__…задают перехват операций (индекс, арифметика, вызов).- Фигурные скобки
{}создают таблицу: можно задать поляkey = valueи/или элементы-массив с индексами с 1.
Функция calculate ведет себя по-разному в зависимости от типов переданных аргументов. Это пример ад-hoc полиморфизма, реализованного через проверку типов.
Полиморфизм в Lua упрощает архитектуру программ, позволяя создавать модульные и расширяемые системы. Разработчики могут добавлять новые классы, не затрагивая существующий код, если новые классы соответствуют ожидаемому интерфейсу.
Код ITЗагрузка примера кода…
Разбор:
functionобъявляет функцию;endзакрывает тело. Имя послеfunctionстановится ссылкой на вызываемый объект.- Синтаксис
obj:method()передаётobjпервым аргументом (self) — удобный стиль для методов таблиц. returnзавершает функцию и может вернуть несколько значений через запятую.setmetatableсвязывает таблицу с метатаблицей; поля__…задают перехват операций (индекс, арифметика, вызов).- Фигурные скобки
{}создают таблицу: можно задать поляkey = valueи/или элементы-массив с индексами с 1. - Ключевые вызовы в фрагменте:
print(),setmetatable(). - При переносе в свой проект сохраните порядок шагов и проверьте результат через
printили отладчик.
Функция generate работает с любыми отчётами, имеющими методы start, render и endReport. Добавление нового типа отчета (например, HTML) не потребует изменения кода генератора.
Полиморфизм в Lua — это мощный инструмент, который делает код гибким, расширяемым и легко поддерживаемым. Он позволяет абстрагироваться от конкретных реализаций и сосредоточиться на общих принципах взаимодействия объектов.
Метатаблицы и библиотеки ООП
Ключевые метаметоды для объектной модели
| Метаметод | Когда вызывается | Типичное ООП-применение |
|---|---|---|
__index | чтение отсутствующего ключа | наследование методов прототипа |
__newindex | запись в отсутствующий ключ | контроль присваивания, proxy |
__call | вызов таблицы как функции | Class() как синтаксический сахар |
__tostring | преобразование в строку | аналог toString() |
__gc | сборка мусора (userdata) | освобождение ресурсов |
local Vector = {}
Vector.__index = Vector
setmetatable(Vector, {
__call = function(_, x, y)
return Vector:new(x, y)
end,
})
function Vector:new(x, y)
return setmetatable({ x = x, y = y }, self)
end
function Vector:__tostring()
return string.format("Vector(%s, %s)", self.x, self.y)
end
local v = Vector(3, 4) -- __call на классе
print(v) -- Vector(3, 4)
Разбор:
__callна классе позволяет писатьVector(3, 4)вместоVector:new(3, 4).__tostringсрабатывает приprint(v)иtostring(v).
Популярные OOP-библиотеки
| Библиотека | Идея | Когда выбирать |
|---|---|---|
Вручную (__index) | минимум зависимостей | скрипты, обучение, встраивание |
| MiddleClass | классы, super, миксины | игры (LÖVE), средние проекты |
| classic | лаконичный синтаксис class:extends() | прототипы, малый код |
| Luau OOP | export type, --!strict | Roblox, типобезопасные API |
В production на Roblox обычно комбинируют Luau-типы с паттерном Component / модулями, а не глубокие иерархии классов — ближе к композиции, чем к Java-наследованию.
Множественное наследование через __index-цепочку
local function mixin(target, ...)
for i = 1, select("#", ...) do
local source = select(i, ...)
for k, v in pairs(source) do
if type(v) == "function" and target[k] == nil then
target[k] = v
end
end
end
end
Разбор:
- Lua позволяет собирать поведение из нескольких таблиц — аналог PHP
trait, но без встроенного разрешения конфликтов. - При коллизии имён нужна явная политика (как
insteadofв PHP).
Сравнение Lua с Java
| Тема | Lua / Luau | Java |
|---|---|---|
| Модель объектов | прототипы (таблицы + метатаблицы) | классы, номинальные типы |
Ключевое слово class | нет (синтаксический сахар только в библиотеках) | есть |
| Создание экземпляра | Class:new() / фабрика | new Class() |
this / self | self первым аргументом; : подставляет | this неявно |
| Приватность | замыкания, local | private в JVM |
| Наследование | цепочка __index | extends (один класс) |
| Интерфейсы | duck typing; в Luau — export type | interface |
| Полиморфизм | по наличию методов | по типу и override |
| Статическая типизация | Luau optional; чистый Lua — нет | всегда |
| Сборка мусора | tracing GC | tracing GC |
Java даёт жёсткий контракт на этапе компиляции; Lua — гибкость в runtime ценой дисциплины в команде (соглашения, Luau strict, тесты).
Типичные ошибки
| Ошибка | Симптом | Что делать |
|---|---|---|
Вызов метода через . вместо : | self nil, поля не находятся | всегда obj:method() для методов |
| Общая таблица в поле класса | все экземпляры делят один массив | создавать {} в new, не в прототипе |
Забыли setmetatable / __index | метод есть в классе, но не у объекта | шаблон Class.__index = Class |
Псевдоприватное поле _x | любой код может записать | замыкание или модуль с local |
| Глубокая цепочка прототипов | сложно отследить, откуда метод | плоские иерархии, композиция |
Копирование таблицы через = | неожиданные мутации | явное клонирование |
| Нет проверки контракта | вызов несуществующего метода в runtime | Luau types, assert в фабрике |
Чеклист проектирования класса в Lua
- Фабрика — один путь создания (
new), все поля инициализированы. - Прототип —
Class.__index = Class(или библиотека делает это за вас). - Состояние — секреты в
localupvalue, не в публичной таблице. - Методы — вызов только через
:; сигнатуры документированы. - Наследование — не глубже 2–3 уровней; иначе миксины/композиция.
- Контракт — в Luau
export type; в чистом Lua — модуль + тесты. - Ресурсы —
close()/__gcдля файлов и соединений.
Для повторения принципов: ООП — о разделе. Смежные материалы: таблицы в Lua, ООП в Java.
Учебные примеры ООП
Небольшие самодостаточные программы, которые показывают классы, объекты, инкапсуляцию, наследование и взаимодействие нескольких типов на одной предметной области.
Класс и объект
Чертёж класса Figure и конкретные объекты — круг и квадрат.
Код ITЗагрузка примера кода…
Банковский счёт
Инкапсуляция: скрытое поле баланса и методы deposit/withdraw.
Код ITЗагрузка примера кода…
Наследование
Родитель Animal и дочерние Cat и Dog с общим eat() и своим speak().
Код ITЗагрузка примера кода…
Смартфон
Состояние объекта: заряд батареи, звонки и подзарядка.
Код ITЗагрузка примера кода…
Студент
Список оценок, средний балл и проходной порог.
Код ITЗагрузка примера кода…
Корзина покупок
Взаимодействие Product, Cart и Order при оформлении заказа.
Код ITЗагрузка примера кода…
Автомобиль
Пробег, расход топлива и напоминание о техобслуживании.
Код ITЗагрузка примера кода…
Пользователь
Скрытый пароль, вход в систему и публикация сообщений.
Код ITЗагрузка примера кода…