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")
endAplique 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, 3Um 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.
Desafio
FácilVamos 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 camposnameedamage)Healer.lua: Crie um mixin com um método:heal(amount)que adiciona o valor aself.healthe 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 auxiliarapplyMixinaqui. 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:
- Nome do personagem (uma string)
- Vida inicial (um número)
- Valor de dano (um número)
- Nome do alvo para atacar (uma string)
- 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: 25Cada 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")
endAplique 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, 3Importante: 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
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
1O Conceito 'Self'
Tabelas com Funções'self' ExplícitoA Sintaxe de Dois-pontosPonto vs Dois-pontosRecapitulação - Movendo o Ponto4Projeto: Banco Digital
Configuração do ProjetoMétodo de Depósito7Polimorfismo e Sobrescrita
Sobrescrita de MétodosChamando Métodos da Classe PaiDuck TypingInterface ComumVerificando o TipoRecapitulação - Papéis de Funcionários10Projeto: Gerenciador de Formas
Configuração do ProjetoClasse Retângulo2Padrão de Protótipo de Classe
O Conceito de ProtótipoVinculando com __indexO Construtor :new()Inicializando AtributosInstâncias IndependentesRecapitulação - Fábrica de Carros5Sobrecarga de Operadores em POO
Somando ObjetosSubtraindo ObjetosConcatenando ObjetosComparando Objetos (<, >)Recapitulação - Matemática de Carteira8Encapsulamento
Convenções de NomenclaturaClosures para PrivacidadeAcesso via ClosuresTabelas Somente LeituraLógica de ValidaçãoRecapitulação - Cofre Seguro11Padrões de Projeto (Lite)
Funções FactoryTabela SingletonPadrão IteratorObserver (Listener)Recapitulação - Logger Factory3Estado e Comportamento do Objeto
Variáveis de InstânciaMétodos GetterMétodos SetterPropriedades CalculadasFormatação de StringsVerificações de IgualdadeRecapitulação - Nota do Aluno6Fundamentos de Herança
Configurando a HerançaHerdando MétodosEstendendo o ConstrutorAdicionando Métodos FilhosCompartilhado vs ÚnicoRecapitulação - Hierarquia de Formas9Composição e Mixins
Relacionamento Tem-UmDelegaçãoMixins SimplesAplicando Múltiplos MixinsMixins vs HerançaRecapitulação - Montagem de Robô