Menu
Coddy logo textTech

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

Часть раздела 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

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

challenge icon

Задание

Легко

Давайте создадим класс 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, покажите его статус, заставьте его атаковать цель, вылечить себя, а затем снова покажите его статус.

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

  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. В этом и заключается сила объединения нескольких примесей!

Шпаргалка

К одному классу можно применить несколько примесей (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: Снова показать статус персонажа
quiz iconПроверьте себя

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

Все уроки раздела Object Oriented Programming