Menu
Coddy logo textTech

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

Часть раздела Объектно-ориентированное программирование путешествия по Lua на Coddy. Урок 52 из 70.

Один mixin добавляет один набор возможностей в class. Но реальным объектам часто нужны возможности из нескольких источников. Игровому объекту может потребоваться перемещаться и быть видимым на экране: это две отдельные задачи, которым не место в одном mixin.

Решение простое: применить несколько mixin к одному и тому же классу. Каждый mixin добавляет свои методы, и в итоге класс получает их все:

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

Примени оба mixin к своему классу, используя ту же вспомогательную функцию, что и раньше:

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

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

challenge icon

Задание

Легко

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

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

  • Attacker.lua: создайте mixin с методом :attack(targetName), который выводит {self.name} attacks {targetName} for {self.damage} damage! (предполагается, что у объекта есть поля name и damage)
  • Healer.lua: создайте mixin с методом :heal(amount), который добавляет значение amount к self.health и выводит {self.name} heals for {amount}. Health: {newHealth}
  • Character.lua: создайте класс Character с конструктором :new(name, health, damage), который сохраняет все три атрибута. Примените оба mixin — Attacker и Healer, чтобы каждый персонаж мог атаковать и исцелять себя. Добавьте метод :status(), который выводит {name} - Health: {health}, Damage: {damage}
  • main.lua: напишите здесь вспомогательную функцию applyMixin. Считайте входные данные: имя персонажа, его здоровье, урон, цель атаки и количество исцеления. Создайте Character, выведите его состояние, заставьте его атаковать цель, исцелить себя, а затем снова выведите его состояние.

Вы получите пять входных данных:

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

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

{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

Каждый mixin добавляет свой метод в класс Character. Персонаж не наследуется ни от одного из mixin. Он просто получает их возможности через функцию applyMixin. В этом и заключается сила объединения нескольких mixin!

Попробуйте сами

-- Основной файл

-- TODO: Напишите вспомогательную функцию applyMixin
-- Эта функция должна копировать все методы из таблицы mixin в целевую таблицу
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 с заданными name, health и damage

-- TODO: Покажите статус персонажа

-- TODO: Пусть персонаж атакует цель

-- TODO: Пусть персонаж исцелит себя

-- TODO: Покажите статус персонажа снова
quiz iconПроверьте себя

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

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

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