Mixins simples
Parte de la sección Object Oriented Programming del Journey de Lua de Coddy — lección 51 de 70.
La composición te permite poner objetos dentro de otros objetos. Pero, ¿qué pasa si quieres compartir comportamiento entre clases no relacionadas?
Un Player y un MenuItem podrían necesitar ambos un método :describe(), pero no comparten una clase padre. Aquí es donde entran los mixins.
Un mixin es simplemente una tabla que contiene funciones. Puedes "mezclar" estas funciones en una clase copiándolas:
local Printable = {}
function Printable:describe()
print("Object: " .. tostring(self))
end
function Printable:log(message)
print("[LOG] " .. message)
endPara aplicar este mixin a una clase, se copia cada función de la tabla del mixin a la tabla de la clase:
local function applyMixin(class, mixin)
for key, value in pairs(mixin) do
class[key] = value
end
end
local Player = {}
Player.__index = Player
applyMixin(Player, Printable) -- Ahora Player tiene :describe() y :log()Después de aplicar el mixin, cualquier instancia de Player puede llamar a esos métodos como si estuvieran definidos directamente en la clase:
local p = setmetatable({}, Player)
p:log("Player created") -- [LOG] Jugador creadoA diferencia de la herencia, los mixins no crean una relación padre-hijo. Simplemente estás copiando funciones de una tabla a otra.
Esto hace que los mixins sean perfectos para compartir comportamientos de utilidad —como el registro (logging), la serialización o los métodos de descripción— entre clases que, de otro modo, no tienen nada en común.
Desafío
Fácil¡Vamos a construir un sistema de etiquetas usando mixins! Crearás un mixin Taggable que se puede aplicar a cualquier clase, otorgando a los objetos la capacidad de gestionar una colección de etiquetas, lo cual es útil para categorizar elementos, publicaciones o cualquier entidad que necesite etiquetas.
Organizarás tu código en tres archivos:
Taggable.lua: Crea una tabla mixin que contenga dos métodos::addTag(tag)— añade una etiqueta aself.tags(asume que la tabla existe):listTags()— imprime todas las etiquetas en el formatoTags: tag1, tag2, tag3(oTags: nonesi está vacía)
Article.lua: Crea una clase Article con un constructor:new(title)que almacene el título e inicialice una tablatagsvacía. Aplica el mixin Taggable para dar a las instancias de Article capacidades de etiquetado. Incluye un método:getTitle()que devuelva el título del artículo.main.lua: Escribe la función auxiliarapplyMixinaquí (la usarás en Article.lua a través de require). Luego lee las entradas para el título de un artículo y dos etiquetas. Crea un Article, añádele ambas etiquetas, imprime el título y enumera todas las etiquetas.
Recibirás tres entradas:
- El título del artículo (una cadena)
- La primera etiqueta a añadir (una cadena)
- La segunda etiqueta a añadir (una cadena)
Tu salida debe mostrar el título del artículo seguido de sus etiquetas:
Title: {title}
Tags: {tag1}, {tag2}Por ejemplo, si las entradas son Lua Basics, programming, y tutorial, la salida debería ser:
Title: Lua Basics
Tags: programming, tutorialRecuerda: los métodos del mixin funcionan porque las instancias de Article tienen una tabla tags sobre la cual los métodos del mixin pueden operar. El mixin no crea esta tabla; tu constructor de Article lo hace. ¡El mixin solo proporciona el comportamiento compartido!
Hoja de referencia
Un mixin es una tabla que contiene funciones que pueden compartirse entre clases no relacionadas sin crear una relación padre-hijo.
Para crear un mixin, define una tabla con métodos:
local Printable = {}
function Printable:describe()
print("Object: " .. tostring(self))
end
function Printable:log(message)
print("[LOG] " .. message)
endPara aplicar un mixin a una clase, copia sus funciones en la tabla de la clase:
local function applyMixin(class, mixin)
for key, value in pairs(mixin) do
class[key] = value
end
end
local Player = {}
Player.__index = Player
applyMixin(Player, Printable)Después de aplicar el mixin, las instancias pueden llamar a los métodos del mixin:
local p = setmetatable({}, Player)
p:log("Player created") -- [LOG] Player createdLos mixins son ideales para compartir comportamientos de utilidad como el registro (logging), la serialización o el etiquetado entre clases que no comparten herencia.
Pruébalo tú mismo
-- Archivo principal para el sistema de etiquetado
local Taggable = require('Taggable')
local Article = require('Article')
-- TODO: Implementar la función applyMixin
-- Esta función debe copiar todos los métodos de una tabla mixin a una tabla de destino
function applyMixin(target, mixin)
-- TODO: Recorrer el mixin y copiar cada método al destino
end
-- Leer entradas
local title = io.read()
local tag1 = io.read()
local tag2 = io.read()
-- TODO: Crear un Artículo con el título dado
-- TODO: Añadir ambas etiquetas al artículo
-- TODO: Imprimir el título en formato: Title: {title}
-- TODO: Listar todas las etiquetas usando el método listTags
Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.
Todas las lecciones de Object Oriented Programming
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 robots