Mixins vs. Herencia
Parte de la sección Object Oriented Programming del Journey de Lua de Coddy — lección 53 de 70.
Has aprendido dos formas de compartir comportamiento: la herencia crea una relación "es-un" a través de la cadena de prototipos, mientras que los mixins copian funciones directamente en una clase. ¿Pero cuándo deberías usar cada enfoque?
Usa la herencia cuando los objetos comparten una identidad fundamental. Un Bird es un Animal; tiene sentido que Bird herede comportamientos animales básicos como :eat() o :sleep(). La relación es permanente y define lo que el objeto es fundamentalmente.
Utiliza mixins cuando quieras añadir una funcionalidad que no defina la identidad central del objeto.
No todas las aves pueden volar; los pingüinos y los avestruces son aves, pero no vuelan. Volar es una capacidad, no un rasgo definitorio de ser un ave. Un mixin Flyable te permite añadir el vuelo a las aves que lo necesitan, sin imponérselo a todas ellas.
Los dos enfoques funcionan de maravilla juntos:
-- Bird hereda de Animal (relación es-un)
local Bird = {}
Bird.__index = Bird
setmetatable(Bird, {__index = Animal})
-- Flyable es una capacidad (mixin)
local Flyable = {}
function Flyable:fly()
print(self.name .. " soars through the sky!")
end
-- Aplica el mixin solo a las aves que pueden volar
applyMixin(Bird, Flyable)Este patrón te ofrece lo mejor de ambos mundos: una jerarquía de clases clara para una identidad compartida, además de capacidades flexibles que pueden integrarse donde sea necesario.
Desafío
Fácil¡Vamos a construir un pequeño ecosistema de criaturas que demuestre cuándo usar la herencia frente a los mixins! Crearás una clase base Animal, un Bird que hereda de ella y un mixin Swimmable—porque aunque todas las aves son animales, no todas las aves pueden nadar.
Organizarás tu código en cuatro archivos:
Animal.lua: Crea la clase base Animal con un constructor:new(name)que almacene el nombre. Incluye un método:eat()que imprima{name} is eating. Esto representa la identidad principal que comparten todos los animales.Swimmable.lua: Crea un mixin con un método:swim()que imprima{self.name} is swimming. Esta es una capacidad—no todas las criaturas pueden nadar, por lo que no debería formar parte de la cadena de herencia.Bird.lua: Crea una clase Bird que herede de Animal. El constructor:new(name, canSwim)debe llamar al constructor padre y almacenar si esta ave puede nadar. Aplica el mixin Swimmable a la clase Bird. Incluye un método:chirp()que imprima{name} chirps!—este es un comportamiento específico de las aves.main.lua: Escribe la función auxiliarapplyMixinaquí. Lee las entradas para el nombre de un ave y si puede nadar (yesono). Crea una instancia de Bird, luego demuestra sus habilidades: llama a:eat()(heredado de Animal), llama a:chirp()(método propio de Bird), y solo llama a:swim()si el ave puede nadar.
Recibirás dos entradas:
- El nombre del ave (una cadena)
- Si el ave puede nadar:
yesono
Si el ave puede nadar, tu salida debería ser:
{name} is eating
{name} chirps!
{name} is swimmingSi el ave no puede nadar, tu salida debería ser:
{name} is eating
{name} chirps!Por ejemplo, si las entradas son Duck y yes, la salida debería ser:
Duck is eating
Duck chirps!
Duck is swimmingSi las entradas son Sparrow and no, la salida debería ser:
Sparrow is eating
Sparrow chirps!Este desafío muestra la distinción clave: Bird es un Animal (herencia), pero nadar es una capacidad opcional (mixin). El mixin se aplica a la clase, ¡pero tu lógica principal decide si usarlo basándose en las habilidades individuales del ave!
Hoja de referencia
Usa la herencia cuando los objetos comparten una identidad fundamental (una relación de tipo "es-un"). Usa mixins al añadir capacidades que no definen la identidad principal del objeto.
La herencia crea una relación permanente a través de la cadena de prototipos:
local Bird = {}
Bird.__index = Bird
setmetatable(Bird, {__index = Animal})Los mixins añaden capacidades opcionales que pueden aplicarse de forma selectiva:
local Flyable = {}
function Flyable:fly()
print(self.name .. " soars through the sky!")
end
applyMixin(Bird, Flyable)Los dos enfoques funcionan juntos: usa la herencia para la identidad compartida y los mixins para capacidades flexibles que solo algunas instancias necesitan.
Pruébalo tú mismo
-- main.lua: Punto de entrada - crear un pájaro y demostrar sus habilidades
-- Función auxiliar para aplicar un mixin a una clase
local function applyMixin(class, mixin)
-- TODO: Copiar todos los métodos del mixin a la clase
end
-- Aplicar el mixin Swimmable a Bird antes de requerir Bird
-- Nota: Es posible que necesites manejar esto en Bird.lua o aquí
local Bird = require('Bird')
local Swimmable = require('Swimmable')
-- Aplicar mixin a la clase Bird
applyMixin(Bird, Swimmable)
-- Leer entradas
local name = io.read()
local canSwimInput = io.read()
-- TODO: Determinar si el pájaro puede nadar basándose en la entrada ("yes" o "no")
-- TODO: Crear una instancia de Bird con el nombre y la capacidad de nadar
-- TODO: Llamar a eat() - heredado de Animal
-- TODO: Llamar a chirp() - método propio de Bird
-- TODO: Solo llamar a swim() si el pájaro puede nadar
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