Логика валидации
Часть раздела Объектно-ориентированное программирование путешествия по Lua на Coddy. Урок 47 из 70.
До сих пор мы рассматривали разные способы защиты данных: соглашения об именовании указывают на назначение, замыкания полностью скрывают переменные, а таблицы, доступные только для чтения, предотвращают любые изменения. Но часто нужно нечто среднее: разрешить изменения, но только допустимые.
Именно здесь методы-сеттеры становятся мощным инструментом. Вместо того чтобы просто присваивать значение, сеттер может проверить, имеет ли новое значение смысл, прежде чем принять его:
local Person = {}
Person.__index = Person
function Person:new(name, age)
local obj = {
_name = name,
_age = age
}
setmetatable(obj, Person)
return obj
end
function Person:setAge(newAge)
if newAge < 0 then
print("Error: Age cannot be negative")
return
end
self._age = newAge
endКогда кто-то вызывает person:setAge(-5), метод обнаруживает недопустимое значение и отказывается обновлять данные. Состояние объекта остаётся согласованным. Без этой защиты неправильные данные могли бы незаметно повредить вашу программу: представьте, что вы рассчитываете пенсионные выплаты с отрицательным возрастом.
Валидация может быть настолько простой или сложной, насколько это необходимо. Можно проверять диапазоны, типы или соответствие значений ожидаемым форматам. Основной принцип заключается в том, что сеттер действует как привратник, гарантируя, что в ваш объект попадают только осмысленные данные.
Задание
ЛегкоСоздадим класс Player, который защищает свои атрибуты здоровья и уровня с помощью логики проверки в методах-сеттерах! Это гарантирует, что игровые персонажи никогда не получат невозможные характеристики.
Вы организуете код в двух файлах:
Player.lua: создайте класс, который управляет характеристиками игрока с корректной проверкой. Конструктор:new(name)должен инициализировать игрока с именем, значением100для_healthи значением1для_level. Включите следующие методы::getName(): возвращает имя игрока:getHealth(): возвращает текущее здоровье:getLevel(): возвращает текущий уровень:setHealth(value): обновляет здоровье, только если значение находится между 0 и 100 (включительно). Если значение недействительно, выведитеError: Health must be between 0 and 100и не изменяйте значение.:setLevel(value): обновляет уровень, только если значение равно 1 или больше. Если значение недействительно, выведитеError: Level must be at least 1и не изменяйте значение.
main.lua: подключите свой модуль Player и считайте имя игрока, затем два значения для проверки сеттеров. Создайте игрока, попытайтесь установить его здоровье в первое значение, попытайтесь установить его уровень во второе значение, затем выведите итоговые характеристики игрока.
Вы получите три входных значения:
- Имя игрока
- Значение здоровья, которое нужно попытаться установить (число, может быть допустимым или недействительным)
- Значение уровня, которое нужно попытаться установить (число, может быть допустимым или недействительным)
В выводе должны отображаться все сообщения об ошибках при недействительных попытках, а затем итоговые характеристики игрока:
Name: {name}
Health: {finalHealth}
Level: {finalLevel}Например, если входными значениями являются Hero, -20 и 5, вывод должен быть таким:
Error: Health must be between 0 and 100
Name: Hero
Health: 100
Level: 5Сеттер здоровья отклонил значение -20 (сохранив здоровье равным 100), а сеттер уровня принял значение 5 как допустимое.
Если входными значениями являются Warrior, 75 и 0, вывод должен быть таким:
Error: Level must be at least 1
Name: Warrior
Health: 75
Level: 1Попробуйте сами
-- Подключить модуль Player
local Player = require('Player')
-- Прочитать входные данные
local name = io.read()
local healthValue = tonumber(io.read())
local levelValue = tonumber(io.read())
-- TODO: Создать нового игрока с заданным именем
-- TODO: Попытаться установить здоровье игрока в healthValue
-- TODO: Попытаться установить уровень игрока в levelValue
-- TODO: Вывести финальную статистику игрока в формате:
-- Имя: {name}
-- Здоровье: {finalHealth}
-- Уровень: {finalLevel}
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Объектно-ориентированное программирование
1Концепция 'self'
Таблицы с функциямиЯвный 'self'Синтаксис с двоеточиемТочка против двоеточияИтоги — Движущаяся точка4Проект: Цифровой банк
Настройка проектаМетод пополнения7Полиморфизм и переопределение
Переопределение методовВызов родительских методовУтиная типизацияОбщий интерфейсПроверка типаИтоги — Роли сотрудников2Паттерн прототипа класса
Концепция прототипаСвязывание через __indexКонструктор :new()Инициализация атрибутовНезависимые экземплярыИтоги — Автозавод5Перегрузка операторов в ООП
Сложение объектовВычитание объектовКонкатенация объектовСравнение объектов (<, >)Повторение — Математика кошелька8Инкапсуляция
Соглашения об именованииЗамыкания для приватностиДоступ через замыканияТаблицы только для чтенияЛогика валидацииИтоги — Защищенное хранилище11Паттерны проектирования (Lite)
Фабричные функцииТаблица SingletonПаттерн IteratorObserver (Слушатель)Итоги — Logger Factory3Состояние и поведение объектов
Переменные экземпляраГеттерыСеттерыВычисляемые свойстваФорматирование строкПроверка на равенствоПовторение — Оценка студента6Основы наследования
Настройка наследованияНаследование методовРасширение конструктораДобавление дочерних методовОбщее и уникальноеИтоги: Иерархия фигурПотренируйтесь самостоятельно: Онлайн-компилятор Lua