Menu
Coddy logo textTech

Extender el constructor

Parte de la sección Programación Orientada a Objetos del Journey de Lua de Coddy. Lección 33 de 70.

Cuando una clase hija hereda de una clase padre, a menudo quieres que el constructor de la hija inicialice todo lo que inicializaría el padre, además de cualquier campo adicional específico de la hija. En lugar de duplicar el código, puedes llamar directamente al constructor del padre.

La técnica consiste en usar el nombre de la clase padre para invocar su método :new(), pasando self como primer argumento. Esto crea el objeto base con toda la inicialización de la clase padre, que después personalizas para la clase hija.

local Vehicle = {}
Vehicle.__index = Vehicle

function Vehicle:new(speed)
    local obj = {speed = speed}
    setmetatable(obj, Vehicle)
    return obj
end

-- Car hereda de Vehicle
local Car = {}
Car.__index = Car
setmetatable(Car, {__index = Vehicle})

function Car:new(speed, brand)
    local obj = Vehicle.new(Vehicle, speed)  -- Llama al constructor del padre
    obj.brand = brand                         -- Añade el campo específico del hijo
    setmetatable(obj, Car)                    -- Re-enlaza a Car
    return obj
end

Observa los pasos clave en el constructor de la clase hija: primero, llamamos a Vehicle.new(Vehicle, speed) para obtener un objeto con el campo speed ya establecido. Después, añadimos el campo específico de la clase hija brand. Finalmente, cambiamos la metatabla de Vehicle a Car para que la instancia busque primero los métodos en la clase hija.

También puedes usar la sintaxis de dos puntos al llamar al constructor principal: Vehicle:new(speed) equivale a Vehicle.new(Vehicle, speed), ya que ambos pasan Vehicle como primer argumento. Cualquiera de las dos formas funciona correctamente aquí, así que puedes usar la que te resulte más legible.

local myCar = Car:new(120, "Toyota")
print(myCar.speed)  -- Salida: 120
print(myCar.brand)  -- Salida: Toyota

Este patrón mantiene tu código DRY: la lógica de inicialización compartida vive en el elemento principal, mientras que cada elemento secundario solo gestiona sus atributos únicos.

challenge icon

Desafío

Fácil

¡Construyamos una jerarquía de criaturas donde una clase child extienda el constructor de su clase parent! Crearás una clase parent Animal y una clase child Bird, cuyo constructor llama al constructor de Animal para gestionar la inicialización compartida antes de añadir su propio atributo único.

Organizarás tu código en tres archivos:

  • Animal.lua: Define tu clase parent con un constructor :new(name) que almacene el nombre del animal. Incluye un método :describe() que imprima el nombre del animal. Configura el patrón de clase estándar con __index y devuelve la clase.
  • Bird.lua: Define tu clase child que hereda de Animal. El constructor :new(name, wingspan) de Bird debe llamar al constructor parent mediante Animal.new(Animal, name) para inicializar el nombre, después añadir el campo wingspan al object y, finalmente, volver a enlazar la metatable con Bird. Incluye un método :info() que imprima tanto el nombre como la envergadura en un formato específico.
  • main.lua: Requiere tu módulo Bird, lee dos inputs (nombre y envergadura), crea una instance de Bird y llama a ambos métodos para demostrar que la funcionalidad heredada y la específica de la clase child funcionan conjuntamente.

Recibirás dos inputs:

  1. El nombre del ave
  2. La envergadura del ave (un número)

En tu archivo main, crea un Bird con el nombre y la envergadura given. Después llama a :describe() seguido de :info() en líneas separadas.

El método :describe() (inherited de Animal) debe imprimir:

Animal: {name}

El método :info() (defined en Bird) debe imprimir:

{name} has a wingspan of {wingspan}

Por ejemplo, si los inputs son Eagle y 200, el output debe ser:

Animal: Eagle
Eagle has a wingspan of 200

La clave aquí es que tu constructor de Bird no duplica la inicialización del nombre. Delega ese trabajo al constructor de Animal, después añade solo lo que es único de las aves. ¡Esto mantiene tu código DRY mientras construyes una cadena de herencia adecuada!

Pruébalo tú mismo

-- main.lua: Punto de entrada

local Bird = require('Bird')

-- Leer entradas
local name = io.read()
local wingspan = tonumber(io.read())

-- TODO: Crear una instancia de Bird con el name y wingspan dados

-- TODO: Llamar a :describe() en el bird (heredado de Animal)

-- TODO: Llamar a :info() en el bird (definido en Bird)
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 Programación Orientada a Objetos

Practica por tu cuenta: Compilador de Lua online