Применение нескольких примесей
Часть раздела Объектно-ориентированное программирование путешествия по 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. В этом и заключается сила объединения нескольких примесей!
Попробуйте сами
-- Основной файл
-- 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: Снова показать статус персонажа
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Объектно-ориентированное программирование
1Концепция 'self'
Таблицы с функциямиЯвный 'self'Синтаксис с двоеточиемТочка против двоеточияИтоги — Движущаяся точка4Проект: Цифровой банк
Настройка проектаМетод пополнения7Полиморфизм и переопределение
Переопределение методовВызов родительских методовУтиная типизацияОбщий интерфейсПроверка типаИтоги — Роли сотрудников2Паттерн прототипа класса
Концепция прототипаСвязывание через __indexКонструктор :new()Инициализация атрибутовНезависимые экземплярыИтоги — Автозавод5Перегрузка операторов в ООП
Сложение объектовВычитание объектовКонкатенация объектовСравнение объектов (<, >)Повторение — Математика кошелька8Инкапсуляция
Соглашения об именованииЗамыкания для приватностиДоступ через замыканияТаблицы только для чтенияЛогика валидацииИтоги — Защищенное хранилище11Паттерны проектирования (Lite)
Фабричные функцииТаблица SingletonПаттерн IteratorObserver (Слушатель)Итоги — Logger Factory3Состояние и поведение объектов
Переменные экземпляраГеттерыСеттерыВычисляемые свойстваФорматирование строкПроверка на равенствоПовторение — Оценка студента6Основы наследования
Настройка наследованияНаследование методовРасширение конструктораДобавление дочерних методовОбщее и уникальноеИтоги: Иерархия фигур9Композиция и примеси
Отношение "Has-A"ДелегированиеПростые примесиПрименение нескольких примесейПримеси против наследованияИтоги — Сборка роботаПотренируйтесь самостоятельно: Онлайн-компилятор Lua