Menu
Coddy logo textTech

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.

challenge icon

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 auxiliar applyMixin aquí. Lee las entradas para el nombre de un ave y si puede nadar (yes o no). 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:

  1. El nombre del ave (una cadena)
  2. Si el ave puede nadar: yes o no

Si el ave puede nadar, tu salida debería ser:

{name} is eating
{name} chirps!
{name} is swimming

Si 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 swimming

Si 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
quiz iconPonte a prueba

Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.

Todas las lecciones de Object Oriented Programming