Menu
Coddy logo textTech

Relación "Tiene-Un"

Parte de la sección Object Oriented Programming del Journey de Lua de Coddy — lección 49 de 70.

A lo largo de este curso, has utilizado la herencia para compartir comportamiento entre clases; un Dog "es un" Animal. Pero la herencia no siempre es la herramienta adecuada.

A veces, los objetos no comparten un árbol genealógico; en su lugar, un objeto simplemente contiene a otro. Esto se llama composición, y modela una relación de tipo "tiene-un" (has-a).

Considera un coche. Un coche no es un tipo de motor; tiene un motor. El motor es un objeto independiente que existe dentro del coche:

local Engine = {}
Engine.__index = Engine

function Engine:new(horsepower)
    local obj = {horsepower = horsepower}
    setmetatable(obj, Engine)
    return obj
end

local Car = {}
Car.__index = Car

function Car:new(model, engine)
    local obj = {
        model = model,
        engine = engine  -- Almacena el objeto Engine dentro de Car
    }
    setmetatable(obj, Car)
    return obj
end

Al crear un coche, pasas un objeto motor existente a su constructor:

local myEngine = Engine:new(200)
local myCar = Car:new("Sedan", myEngine)

print(myCar.engine.horsepower)  -- 200

El coche no hereda del motor; simplemente mantiene una referencia a uno. Esto mantiene ambas clases independientes. Podrías cambiarlo por un motor diferente o usar la misma clase de motor en una motocicleta.

La composición te da una flexibilidad que la herencia no puede: estás ensamblando objetos a partir de partes en lugar de encerrarlos en una jerarquía familiar rígida.

challenge icon

Desafío

Fácil

¡Vamos a construir un sistema informático utilizando composición! En lugar de herencia, crearás una clase Computer que tiene una CPU en su interior, demostrando la relación "tiene-un" (has-a).

Organizarás tu código en tres archivos:

  • CPU.lua: Crea una clase CPU con un constructor :new(brand, speed) que almacene el nombre de la marca y la velocidad en GHz. Incluye un método :getInfo() que devuelva una cadena con el formato: {brand} @ {speed}GHz
  • Computer.lua: Crea una clase Computer cuyo constructor :new(model, cpu) acepte un nombre de modelo y un objeto CPU. La clase Computer almacena el objeto CPU en su interior (¡composición!). Incluye un método :describe() que imprima el modelo de la computadora y la información de su CPU.
  • main.lua: ¡Une todo! Lee las entradas para la marca de la CPU, la velocidad de la CPU y el modelo de la computadora. Crea primero un objeto CPU y luego pásalo al constructor de Computer. Finalmente, llama al método :describe() de la computadora.

Recibirás tres entradas:

  1. La marca de la CPU (una cadena)
  2. La velocidad de la CPU en GHz (un número)
  3. El nombre del modelo de la computadora (una cadena)

Tu salida debe mostrar la descripción de la computadora, que incluye información de su CPU contenida:

Model: {model}
CPU: {brand} @ {speed}GHz

Por ejemplo, si las entradas son Intel, 3.5, y WorkStation, la salida debería ser:

Model: WorkStation
CPU: Intel @ 3.5GHz

Observa cómo la clase Computer no hereda de CPU; simplemente mantiene una referencia a una. El método :describe() de Computer accede a los datos de la CPU a través del objeto almacenado, ¡mostrando cómo la composición te permite construir objetos complejos a partir de partes más simples!

Hoja de referencia

Los objetos pueden relacionarse a través de la composición, que modela una relación de tipo "tiene un". En lugar de la herencia, un objeto contiene a otro como un componente.

Ejemplo de composición con un coche que tiene un motor:

local Engine = {}
Engine.__index = Engine

function Engine:new(horsepower)
    local obj = {horsepower = horsepower}
    setmetatable(obj, Engine)
    return obj
end

local Car = {}
Car.__index = Car

function Car:new(model, engine)
    local obj = {
        model = model,
        engine = engine  -- Almacena el objeto Engine dentro de Car
    }
    setmetatable(obj, Car)
    return obj
end

Creación y uso de objetos compuestos:

local myEngine = Engine:new(200)
local myCar = Car:new("Sedan", myEngine)

print(myCar.engine.horsepower)  -- 200

La composición proporciona flexibilidad al ensamblar objetos a partir de partes en lugar de utilizar jerarquías de herencia rígidas. Los componentes permanecen independientes y pueden reutilizarse en diferentes clases.

Pruébalo tú mismo

-- main.lua
-- Reúne todo utilizando composición

local CPU = require('CPU')
local Computer = require('Computer')

-- Leer entradas
local cpuBrand = io.read()
local cpuSpeed = tonumber(io.read())
local computerModel = io.read()

-- TODO: Crear un objeto CPU con la marca y la velocidad

-- TODO: Crear un objeto Computer con el modelo y el objeto CPU

-- TODO: Llamar al método describe() de la computadora para imprimir la salida
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