Menu
CoddyTech

Применение нескольких примесей

Часть раздела Объектно-ориентированное программирование путешествия по 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

Одно предостережение: если два миксина определяют метод с одинаковым именем, второй применённый миксин перезапишет первый. Используй для методов миксинов разные имена, чтобы избежать неожиданного поведения.

challenge icon

Задание

Легко

Давай создадим класс Character для игры, который объединяет несколько возможностей с помощью миксинов! Твой персонаж сможет и атаковать врагов, и лечить себя: это два отдельных поведения, предоставляемых разными миксинами.

Ты организуешь код в четырёх файлах:

  • Attacker.lua: создай миксин с методом :attack(targetName), который выводит {self.name} attacks {targetName} for {self.damage} damage! (предполагается, что у объекта есть поля name и damage)
  • Healer.lua: создай миксин с методом :heal(amount), который прибавляет 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, покажи его состояние, атакуй цель, вылечи персонажа, а затем снова покажи его состояние.

На вход будут поданы пять значений:

  1. Имя персонажа (строка)
  2. Начальное здоровье (число)
  3. Значение урона (число)
  4. Имя цели для атаки (строка)
  5. Количество здоровья для восстановления (число)

В выводе должно быть показано, как персонаж использует возможности обоих миксинов:

{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: Снова показать состояние персонажа
quiz iconПроверьте себя

В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.

Все уроки раздела Объектно-ориентированное программирование

Потренируйтесь самостоятельно: Онлайн-компилятор Lua