Aplicando Múltiplos Mixins
Parte da seção Programação orientada a objetos 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 muitas vezes precisam de recursos de várias fontes. Um objeto de jogo pode precisar se movimentar e ficar visível na tela: duas responsabilidades distintas que não pertencem ao mesmo mixin.
A solução é simples: aplique vários mixins à mesma classe. Cada mixin contribui com seus próprios métodos, e a classe acaba tendo 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, as 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() -- Exibindo em 5, 3Um cuidado: se dois mixins definirem o mesmo nome de método, o segundo aplicado substituirá o primeiro. Mantenha os métodos dos seus mixins distintos para evitar comportamentos inesperados.
Desafio
FácilVamos criar uma classe Character para um jogo que combina várias capacidades usando mixins! Seu personagem poderá tanto atacar inimigos quanto curar a si mesmo: dois comportamentos distintos que vêm de mixins separados.
Você vai organizar seu código em quatro arquivos:
Attacker.lua: Crie um mixin com um método:attack(targetName)que exibe{self.name} attacks {targetName} for {self.damage} damage!(presume que o objeto tenha os camposnameedamage)Healer.lua: Crie um mixin com um método:heal(amount)que adiciona a quantidade aself.healthe exibe{self.name} heals for {amount}. Health: {newHealth}Character.lua: Crie uma classe Character com um construtor:new(name, health, damage)que armazene os três atributos. Aplique os mixins Attacker e Healer para que cada personagem possa atacar e curar. Inclua um método:status()que exibe{name} - Health: {health}, Damage: {damage}main.lua: Escreva aqui a função auxiliarapplyMixin. Leia os dados de entrada para o nome, a vida e o dano de um personagem, um alvo para atacar e uma quantidade de cura. Crie um Character, mostre seu status, faça com que ataque o alvo, cure a si mesmo e, em seguida, mostre seu status novamente.
Você receberá cinco entradas:
- Nome do personagem (uma string)
- Vida inicial (um número)
- Valor do dano (um número)
- Nome do alvo para atacar (uma string)
- Quantidade de cura (um número)
Sua saída deve mostrar o personagem usando as habilidades de ambos os mixins:
{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 deverá 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 adquire suas habilidades por meio da função applyMixin. Esse é o poder de combinar vários mixins!
Experimente você mesmo
-- Arquivo principal
-- TODO: Escreva a função auxiliar applyMixin
-- Esta função deve copiar todos os métodos de uma tabela de mixin para uma tabela de destino
local function applyMixin(target, mixin)
-- TODO: Implemente a aplicação do mixin
end
-- Disponibilize applyMixin globalmente para que Character.lua possa usá-la
_G.applyMixin = applyMixin
local Character = require('Character')
-- Leia as 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: Crie um Character com o nome, a vida e o dano fornecidos
-- TODO: Mostre o status do personagem
-- TODO: Faça o personagem atacar o alvo
-- TODO: Faça o personagem se curar
-- TODO: Mostre novamente o status do personagem
Esta lição inclui um quiz rápido. Comece a lição para respondê-lo e acompanhar seu progresso.
Todas as lições de Programação orientada a objetos
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ânguloClasse CírculoMétodo PerímetroColeção de FormasÁrea TotalFiltrar Formas2Padrã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ôPratique por conta própria: Compilador de Lua online