Aplicación de múltiples Mixins
Parte de la sección Programación Orientada a Objetos del Journey de Lua de Coddy. Lección 52 de 70.
Un solo mixin añade un conjunto de comportamientos a una clase. Pero los objetos reales a menudo necesitan capacidades de varias fuentes. Un objeto del juego podría necesitar moverse por el entorno y ser visible en la pantalla: dos responsabilidades independientes que no pertenecen al mismo mixin.
La solución es sencilla: aplica varios mixins a la misma clase. Cada mixin aporta sus propios métodos, y la clase termina teniendo todos ellos:
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")
endAplica ambos mixins a tu clase usando la misma función 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)Ahora las instancias de GameObject pueden moverse y mostrarse:
local obj = setmetatable({x = 0, y = 0}, GameObject)
obj:move(5, 3)
obj:show() -- Mostrando en 5, 3Una precaución: si dos mixins definen el mismo nombre de método, el segundo que se aplique sobrescribirá al primero. Mantén distintos los métodos de tus mixins para evitar un comportamiento inesperado.
Desafío
Fácil¡Construyamos una clase Character para un juego que combine varias capacidades mediante mixins! Tu personaje podrá atacar a enemigos y curarse: dos comportamientos distintos que provienen de mixins separados.
Organizarás tu código en cuatro archivos:
Attacker.lua: crea un mixin con un método:attack(targetName)que imprime{self.name} attacks {targetName} for {self.damage} damage!(supone que el objeto tiene los camposnameydamage)Healer.lua: crea un mixin con un método:heal(amount)que suma la cantidad aself.healthe imprime{self.name} heals for {amount}. Health: {newHealth}Character.lua: crea una clase Character con un constructor:new(name, health, damage)que almacene los tres atributos. Aplica los mixins Attacker y Healer para que todos los personajes puedan atacar y curarse. Incluye un método:status()que imprima{name} - Health: {health}, Damage: {damage}main.lua: escribe aquí la función auxiliarapplyMixin. Lee las entradas correspondientes al nombre, la salud y el daño de un personaje, a un objetivo para atacar y a una cantidad para curarse. Crea un Character, muestra su estado, haz que ataque al objetivo, haz que se cure y después muestra su estado nuevamente.
Recibirás cinco entradas:
- Nombre del personaje (una cadena)
- Salud inicial (un número)
- Valor de daño (un número)
- Nombre del objetivo que se atacará (una cadena)
- Cantidad que se curará (un número)
Tu salida debe mostrar al personaje usando ambas capacidades de los mixins:
{name} - Health: {health}, Damage: {damage}
{name} attacks {target} for {damage} damage!
{name} heals for {healAmount}. Health: {newHealth}
{name} - Health: {newHealth}, Damage: {damage}Por ejemplo, si las entradas son Warrior, 100, 25, Goblin y 30, la salida debe ser:
Warrior - Health: 100, Damage: 25
Warrior attacks Goblin for 25 damage!
Warrior heals for 30. Health: 130
Warrior - Health: 130, Damage: 25Cada mixin aporta su propio método a la clase Character. El personaje no hereda de ninguno de los dos mixins. Simplemente obtiene sus capacidades mediante la función applyMixin. ¡Este es el poder de combinar varios mixins!
Pruébalo tú mismo
-- Archivo principal
-- TODO: Escribe la función auxiliar applyMixin
-- Esta función debería copiar todos los métodos de una tabla mixin a una tabla target
local function applyMixin(target, mixin)
-- TODO: Implementa la aplicación del mixin
end
-- Haz que applyMixin esté disponible globalmente para que Character.lua pueda usarlo
_G.applyMixin = applyMixin
local Character = require('Character')
-- Leer las 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: Crea un Character con el name, health y damage dados
-- TODO: Muestra el estado del character
-- TODO: Haz que el character ataque al target
-- TODO: Haz que el character se cure a sí mismo
-- TODO: Muestra el estado del character de nuevo
Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.
Todas las lecciones de Programación Orientada a Objetos
1El concepto de 'self'
Tablas con funciones'self' explícitoLa sintaxis de dos puntosPunto vs. dos puntosResumen - Punto en movimiento4Proyecto: Banco Digital
Configuración del proyectoMétodo de depósito2Patrón de prototipo de clase
El concepto de prototipoVinculación con __indexEl constructor :new()Inicialización de atributosInstancias independientesResumen - Fábrica de coches5Sobrecarga de operadores en POO
Sumar objetosRestar objetosConcatenar objetosComparar objetos (<, >)Resumen - Matemáticas de la billetera8Encapsulamiento
Convenciones de nomenclaturaClosures para privacidadAcceso mediante ClosuresTablas de solo lecturaLógica de validaciónResumen - Bóveda segura11Patrones de diseño (Lite)
Funciones FactoryTabla SingletonPatrón IteradorObserver (Listener)Resumen - Logger Factory3Estado y Comportamiento de los Objetos
Variables de InstanciaMétodos GetterMétodos SetterPropiedades CalculadasFormateo de StringsComprobaciones de IgualdadResumen - Calificación del Estudiante6Fundamentos de la herencia
Configuración de la herenciaHerencia de métodosExtender el constructorAñadir métodos hijosCompartido vs. ÚnicoResumen: Jerarquía de formas9Composición y Mixins
Relación "Tiene-Un"DelegaciónMixins simplesAplicación de múltiples MixinsMixins vs. HerenciaResumen - Ensamblaje de robotsPractica por tu cuenta: Compilador de Lua online