Применение нескольких примесей
Часть раздела Object Oriented Programming путешествия по Lua на Coddy — урок 52 из 70.
Один миксин добавляет один набор поведений классу. Но реальным объектам часто требуются возможности из нескольких источников. Игровому объекту может потребоваться перемещаться и быть видимым на экране — это две разные задачи, которые не должны находиться в одном и том же миксине.
Решение простое: примените несколько примесей к одному и тому же классу. Каждая примесь добавляет свои собственные методы, и в итоге класс получает их все:
local Movable = {}
function Movable:move(dx, dy)
self.x = self.x + dx
self.y = self.y + dy
end
local Visible = {}
function Visible:show()
print("Showing at " .. self.x .. ", " .. self.y)
end
function Visible:hide()
print("Hidden")
endПримените оба миксина к своему классу, используя ту же вспомогательную функцию, что и раньше:
local function applyMixin(class, mixin)
for key, value in pairs(mixin) do
class[key] = value
end
end
local GameObject = {}
GameObject.__index = GameObject
applyMixin(GameObject, Movable)
applyMixin(GameObject, Visible)Теперь экземпляры GameObject могут как перемещаться, так и отображать себя:
local obj = setmetatable({x = 0, y = 0}, GameObject)
obj:move(5, 3)
obj:show() -- Отображение в 5, 3Одно предостережение: если два миксина определяют одно и то же имя метода, то второй примененный миксин перезапишет первый. Делайте методы ваших миксинов уникальными, чтобы избежать неожиданного поведения.
Задание
ЛегкоДавайте создадим класс Character для игры, который объединяет несколько возможностей с помощью примесей (mixins)! Ваш персонаж сможет как атаковать врагов, так и лечить себя — это два различных поведения, которые происходят из отдельных примесей.
Вы организуете свой код в четырех файлах:
Attacker.lua: Создайте примесь с методом:attack(targetName), который выводит{self.name} attacks {targetName} for {self.damage} damage!(предполагается, что у объекта есть поляnameиdamage)Healer.lua: Создайте примесь с методом:heal(amount), который добавляет указанное количество кself.healthи выводит{self.name} heals for {amount}. Health: {newHealth}Character.lua: Создайте класс Character с конструктором:new(name, health, damage), который сохраняет все три атрибута. Примените обе примеси Attacker и Healer, чтобы каждый персонаж мог атаковать и лечиться. Включите метод:status(), который выводит{name} - Health: {health}, Damage: {damage}main.lua: Напишите здесь вспомогательную функциюapplyMixin. Считайте входные данные для имени персонажа, здоровья, урона, цели для атаки и количества исцеления. Создайте объект Character, покажите его статус, заставьте его атаковать цель, вылечить себя, а затем снова покажите его статус.
Вы получите пять входных данных:
- Имя персонажа (строка)
- Начальное здоровье (число)
- Значение урона (число)
- Имя цели для атаки (строка)
- Количество для исцеления (число)
Ваш вывод должен показывать персонажа, использующего способности обеих примесей:
{name} - Health: {health}, Damage: {damage}
{name} attacks {target} for {damage} damage!
{name} heals for {healAmount}. Health: {newHealth}
{name} - Health: {newHealth}, Damage: {damage}Например, если входные данные: Warrior, 100, 25, Goblin и 30, вывод должен быть следующим:
Warrior - Health: 100, Damage: 25
Warrior attacks Goblin for 25 damage!
Warrior heals for 30. Health: 130
Warrior - Health: 130, Damage: 25Каждая примесь добавляет свой собственный метод в класс Character. Персонаж не наследуется ни от одной из примесей — он просто получает их способности через функцию applyMixin. В этом и заключается сила объединения нескольких примесей!
Шпаргалка
К одному классу можно применить несколько примесей (mixins), чтобы объединить различные наборы поведения. Каждая примесь добавляет в класс свои собственные методы.
Определите отдельные примеси для различных задач:
local Movable = {}
function Movable:move(dx, dy)
self.x = self.x + dx
self.y = self.y + dy
end
local Visible = {}
function Visible:show()
print("Showing at " .. self.x .. ", " .. self.y)
end
function Visible:hide()
print("Hidden")
endПримените несколько примесей к классу, используя вспомогательную функцию applyMixin:
local function applyMixin(class, mixin)
for key, value in pairs(mixin) do
class[key] = value
end
end
local GameObject = {}
GameObject.__index = GameObject
applyMixin(GameObject, Movable)
applyMixin(GameObject, Visible)Теперь класс обладает методами из всех примененных примесей:
local obj = setmetatable({x = 0, y = 0}, GameObject)
obj:move(5, 3)
obj:show() -- Showing at 5, 3Важно: Если две примеси определяют метод с одинаковым именем, вторая примененная примесь перезапишет первую. Старайтесь делать имена методов уникальными в разных примесях, чтобы избежать конфликтов.
Попробуйте сами
-- Основной файл
-- TODO: Написать вспомогательную функцию applyMixin
-- Эта функция должна копировать все методы из таблицы mixin в целевую таблицу target
local function applyMixin(target, mixin)
-- TODO: Реализовать применение примеси (mixin)
end
-- Сделать applyMixin доступной глобально, чтобы Character.lua мог её использовать
_G.applyMixin = applyMixin
local Character = require('Character')
-- Чтение входных данных
local name = io.read()
local health = tonumber(io.read())
local damage = tonumber(io.read())
local targetName = io.read()
local healAmount = tonumber(io.read())
-- TODO: Создать персонажа (Character) с заданным именем, здоровьем и уроном
-- TODO: Показать статус персонажа
-- TODO: Заставить персонажа атаковать цель
-- TODO: Заставить персонажа исцелить себя
-- TODO: Снова показать статус персонажа
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Object Oriented Programming
1Концепция 'self'
Таблицы с функциямиЯвный 'self'Синтаксис с двоеточиемТочка против двоеточияИтоги — Движущаяся точка4Проект: Цифровой банк
Настройка проектаМетод пополнения7Полиморфизм и переопределение
Переопределение методовВызов родительских методовУтиная типизацияОбщий интерфейсПроверка типаИтоги — Роли сотрудников2Паттерн прототипа класса
Концепция прототипаСвязывание через __indexКонструктор :new()Инициализация атрибутовНезависимые экземплярыИтоги — Автозавод5Перегрузка операторов в ООП
Сложение объектовВычитание объектовКонкатенация объектовСравнение объектов (<, >)Повторение — Математика кошелька8Инкапсуляция
Соглашения об именованииЗамыкания для приватностиДоступ через замыканияТаблицы только для чтенияЛогика валидацииИтоги — Защищенное хранилище11Паттерны проектирования (Lite)
Фабричные функцииТаблица SingletonПаттерн IteratorObserver (Слушатель)Итоги — Logger Factory3Состояние и поведение объектов
Переменные экземпляраГеттерыСеттерыВычисляемые свойстваФорматирование строкПроверка на равенствоПовторение — Оценка студента6Основы наследования
Настройка наследованияНаследование методовРасширение конструктораДобавление дочерних методовОбщее и уникальноеИтоги: Иерархия фигур9Композиция и примеси
Отношение "Has-A"ДелегированиеПростые примесиПрименение нескольких примесейПримеси против наследованияИтоги — Сборка робота