Menu
Coddy logo textTech

Aplicando Múltiplos Mixins

Parte da seção Object Oriented Programming do Journey de Lua da Coddy — lição 52 de 70.

Um único mixin adiciona um conjunto de comportamentos a uma classe. Mas objetos reais frequentemente precisam de capacidades de múltiplas fontes. Um objeto de jogo pode precisar se mover e ser visível na tela — duas preocupações separadas que não pertencem ao mesmo mixin.

A solução é simples: aplique múltiplos mixins à mesma classe. Cada mixin contribui com seus próprios métodos, e a classe acaba com todos eles:

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

Aplique ambos os mixins à sua classe usando a mesma função auxiliar de antes:

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)

Agora instâncias de GameObject podem tanto se mover quanto se exibir:

local obj = setmetatable({x = 0, y = 0}, GameObject)
obj:move(5, 3)
obj:show()  -- Mostrando em 5, 3

Um aviso: se dois mixins definirem o mesmo nome de método, o segundo aplicado sobrescreverá o primeiro. Mantenha os métodos do seu mixin distintos para evitar comportamentos inesperados.

challenge icon

Desafio

Fácil

Vamos construir uma classe Character para um jogo que combina múltiplas capacidades usando mixins! Seu personagem será capaz de atacar inimigos e se curar — dois comportamentos distintos que vêm de mixins separados.

Você organizará seu código em quatro arquivos:

  • Attacker.lua: Crie um mixin com um método :attack(targetName) que imprime {self.name} attacks {targetName} for {self.damage} damage! (assume que o objeto possui os campos name e damage)
  • Healer.lua: Crie um mixin com um método :heal(amount) que adiciona o valor a self.health e imprime {self.name} heals for {amount}. Health: {newHealth}
  • Character.lua: Crie uma classe Character com um construtor :new(name, health, damage) que armazena todos os três atributos. Aplique os mixins Attacker e Healer para que cada personagem possa atacar e curar. Inclua um método :status() que imprime {name} - Health: {health}, Damage: {damage}
  • main.lua: Escreva a função auxiliar applyMixin aqui. Leia as entradas para o nome de um personagem, vida, dano, um alvo para atacar e uma quantidade de cura. Crie um Character, mostre seu status, faça-o atacar o alvo, curar-se e, em seguida, mostre seu status novamente.

Você receberá cinco entradas:

  1. Nome do personagem (uma string)
  2. Vida inicial (um número)
  3. Valor de dano (um número)
  4. Nome do alvo para atacar (uma string)
  5. Quantidade para curar (um número)

Sua saída deve mostrar o personagem usando ambas as habilidades do mixin:

{name} - Health: {health}, Damage: {damage}
{name} attacks {target} for {damage} damage!
{name} heals for {healAmount}. Health: {newHealth}
{name} - Health: {newHealth}, Damage: {damage}

Por exemplo, se as entradas forem Warrior, 100, 25, Goblin, e 30, a saída deve ser:

Warrior - Health: 100, Damage: 25
Warrior attacks Goblin for 25 damage!
Warrior heals for 30. Health: 130
Warrior - Health: 130, Damage: 25

Cada mixin contribui com seu próprio método para a classe Character. O personagem não herda de nenhum dos mixins — ele simplesmente ganha suas habilidades através da função applyMixin. Este é o poder de combinar múltiplos mixins!

Folha de consulta

Múltiplos mixins podem ser aplicados a uma única classe para combinar diferentes conjuntos de comportamentos. Cada mixin contribui com seus próprios métodos para a classe.

Defina mixins separados para diferentes preocupações:

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

Aplique múltiplos mixins a uma classe usando a função auxiliar 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)

A classe agora possui métodos de todos os mixins aplicados:

local obj = setmetatable({x = 0, y = 0}, GameObject)
obj:move(5, 3)
obj:show()  -- Mostrando em 5, 3

Importante: Se dois mixins definirem o mesmo nome de método, o segundo mixin aplicado sobrescreverá o primeiro. Mantenha os nomes dos métodos distintos entre os mixins para evitar conflitos.

Experimente você mesmo

-- Arquivo principal

-- TODO: Escrever a função auxiliar applyMixin
-- Esta função deve copiar todos os métodos de uma tabela mixin para uma tabela de destino
local function applyMixin(target, mixin)
    -- TODO: Implementar a aplicação do mixin
end

-- Tornar applyMixin disponível globalmente para que o Character.lua possa usá-lo
_G.applyMixin = applyMixin

local Character = require('Character')

-- Ler entradas
local name = io.read()
local health = tonumber(io.read())
local damage = tonumber(io.read())
local targetName = io.read()
local healAmount = tonumber(io.read())

-- TODO: Criar um Character com o nome, vida e dano fornecidos

-- TODO: Mostrar o status do personagem

-- TODO: Fazer o personagem atacar o alvo

-- TODO: Fazer o personagem se curar

-- TODO: Mostrar o status do personagem novamente
quiz iconTeste seus conhecimentos

Esta lição inclui um quiz rápido. Comece a lição para respondê-lo e acompanhar seu progresso.

Todas as lições de Object Oriented Programming