Mixins simples
Parte de la sección Programación orientada a objetos del Journey de Lua de Coddy. Lección 51 de 70.
La composición te permite colocar objetos dentro de otros objetos. Pero ¿qué pasa si quieres compartir comportamiento entre clases no relacionadas?
Tanto Player como MenuItem podrían necesitar un método :describe(), pero no comparten una clase principal. Aquí es donde entran en juego los mixins.
Un mixin es simplemente una tabla que contiene funciones. «Mezclas» 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 esta mezcla a una clase, copia cada función de la tabla de la mezcla 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) -- 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] Player 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 métodos de registro, serialización o descripción, entre clases que, de otro modo, no tendrían nada en común.
Desafío
Fácil¡Vamos a crear un sistema de etiquetas usando mixins! Crearás un mixin Taggable que se puede aplicar a cualquier clase y que permite que los objetos administren una colección de etiquetas, ú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): agrega una etiqueta aself.tags(se supone 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. Incluye un método:getTitle()que devuelva el título del artículo.main.lua: Escribe aquí la función auxiliarapplyMixiny luego aplica el mixin conapplyMixin(Article, Taggable)para dar capacidades de etiquetado a las instancias de Article. Después, lee los datos de entrada para el título de un artículo y dos etiquetas. Crea un Article, agrégale ambas etiquetas, imprime el título y enumera todas las etiquetas.
Recibirás tres datos de entrada:
- El título del artículo (una cadena)
- La primera etiqueta que se agregará (una cadena)
- La segunda etiqueta que se agregará (una cadena)
Tu salida debe mostrar el título del artículo seguido de sus etiquetas:
Title: {title}
Tags: {tag1}, {tag2}Por ejemplo, si los datos de entrada 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 con la que pueden operar los métodos del mixin. El mixin no crea esta tabla. La crea el constructor de Article. ¡El mixin solo proporciona el comportamiento compartido!
Pruébalo tú mismo
-- Archivo principal del sistema de etiquetas
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 mixin y copiar cada método a target
end
-- TODO: Aplicar el mixin Taggable a Article usando applyMixin
-- Leer las entradas
local title = io.read()
local tag1 = io.read()
local tag2 = io.read()
-- TODO: Crear un Article con el título proporcionado
-- TODO: Añadir ambas etiquetas al artículo
-- TODO: Imprimir el título con el formato: Title: {title}
-- TODO: Enumerar 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 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