Вычисляемые свойства
Часть раздела Object Oriented Programming путешествия по Lua на Coddy — урок 15 из 70.
Геттеры и сеттеры обеспечивают доступ к сохраненным значениям, но иногда вам нужны данные, которые не существуют в виде сохраненного поля — их нужно вычислять на основе других атрибутов. Они называются вычисляемыми свойствами.
Рассмотрим прямоугольник. Вы храните его ширину и высоту, но площадь нигде не хранится — она вычисляется путем умножения этих двух значений. Вместо того чтобы хранить избыточные данные, вы создаете метод, который вычисляет и возвращает результат:
local Rectangle = {}
Rectangle.__index = Rectangle
function Rectangle:new(width, height)
local instance = {}
setmetatable(instance, self)
instance.width = width
instance.height = height
return instance
end
function Rectangle:getArea()
return self.width * self.height
endМетод :getArea() не возвращает сохраненное значение — он вычисляет площадь заново при каждом вызове. У этого подхода есть ключевое преимущество: если ширина или высота изменяются, площадь автоматически отражает новые размеры без необходимости что-либо обновлять вручную.
local box = Rectangle:new(5, 3)
print(box:getArea()) -- Вывод: 15
box.width = 10
print(box:getArea()) -- Вывод: 30Вычисляемые свойства обеспечивают согласованность ваших объектов. Вместо того чтобы хранить значения, которые зависят от других полей (и рисковать тем, что они устареют), вы вычисляете их по запросу. Этот паттерн применим к любым производным данным — периметрам, средним значениям, отформатированным строкам или любому значению, которое может быть определено на основе существующих атрибутов.
Задание
ЛегкоДавайте создадим класс Box, который вычисляет свой объем по запросу! Вместо того чтобы хранить объем как поле, вы создадите вычисляемое свойство, которое рассчитывает его на основе размеров коробки всякий раз, когда это необходимо.
Вы организуете свой код в двух файлах:
Box.lua: Определите классBox, используя стандартный паттерн прототипа. Каждая коробка хранит три измерения:width,heightиdepth. Включите:- Конструктор
:new(width, height, depth), который сохраняет все три измерения - Метод
:getVolume(), который вычисляет и возвращает объем (width × height × depth) - Метод
:getSurfaceArea(), который вычисляет и возвращает общую площадь поверхности
- Конструктор
main.lua: Подключите модуль Box, создайте экземпляр коробки и продемонстрируйте, как вычисляемые свойства автоматически отражают текущие размеры.
Площадь поверхности коробки вычисляется по формуле: 2 × (width×height + height×depth + width×depth)
Вы получите три входных значения, представляющих размеры коробки:
- Ширина (Width)
- Высота (Height)
- Глубина (Depth)
В основном файле создайте коробку с заданными размерами, затем выведите две строки:
- Объем, используя
:getVolume() - Площадь поверхности, используя
:getSurfaceArea()
Например, если входные данные равны 3, 4 и 5, вывод должен быть следующим:
60
94Прелесть вычисляемых свойств заключается в том, что они всегда остаются точными. Если вы позже измените один из размеров коробки, повторный вызов :getVolume() автоматически вернет правильное новое значение — никаких ручных обновлений не требуется!
Шпаргалка
Вычисляемые свойства — это методы, которые вычисляют и возвращают значения на основе других атрибутов, а не хранят их напрямую. Это обеспечивает согласованность данных и устраняет избыточность.
Пример вычисляемого свойства для площади:
local Rectangle = {}
Rectangle.__index = Rectangle
function Rectangle:new(width, height)
local instance = {}
setmetatable(instance, self)
instance.width = width
instance.height = height
return instance
end
function Rectangle:getArea()
return self.width * self.height
endМетод :getArea() вычисляет площадь при каждом вызове, автоматически отражая любые изменения ширины или высоты:
local box = Rectangle:new(5, 3)
print(box:getArea()) -- Output: 15
box.width = 10
print(box:getArea()) -- Output: 30Вычисляемые свойства полезны для любых производных данных, таких как периметры, объемы, средние значения или отформатированные строки, которые могут быть определены на основе существующих атрибутов.
Попробуйте сами
-- Подключить модуль Box
local Box = require('Box')
-- Прочитать входные размеры
local width = tonumber(io.read())
local height = tonumber(io.read())
local depth = tonumber(io.read())
-- TODO: Создать экземпляр коробки с заданными размерами
-- TODO: Вывести объем, используя :getVolume()
-- TODO: Вывести площадь поверхности, используя :getSurfaceArea()
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Object Oriented Programming
1Концепция 'self'
Таблицы с функциямиЯвный 'self'Синтаксис с двоеточиемТочка против двоеточияИтоги — Движущаяся точка4Проект: Цифровой банк
Настройка проектаМетод пополнения7Полиморфизм и переопределение
Переопределение методовВызов родительских методовУтиная типизацияОбщий интерфейсПроверка типаИтоги — Роли сотрудников2Паттерн прототипа класса
Концепция прототипаСвязывание через __indexКонструктор :new()Инициализация атрибутовНезависимые экземплярыИтоги — Автозавод5Перегрузка операторов в ООП
Сложение объектовВычитание объектовКонкатенация объектовСравнение объектов (<, >)Повторение — Математика кошелька8Инкапсуляция
Соглашения об именованииЗамыкания для приватностиДоступ через замыканияТаблицы только для чтенияЛогика валидацииИтоги — Защищенное хранилище11Паттерны проектирования (Lite)
Фабричные функцииТаблица SingletonПаттерн IteratorObserver (Слушатель)Итоги — Logger Factory3Состояние и поведение объектов
Переменные экземпляраГеттерыСеттерыВычисляемые свойстваФорматирование строкПроверка на равенствоПовторение — Оценка студента6Основы наследования
Настройка наследованияНаследование методовРасширение конструктораДобавление дочерних методовОбщее и уникальноеИтоги: Иерархия фигур