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
endAl 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) -- 200El 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.
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}GHzComputer.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:
- La marca de la CPU (una cadena)
- La velocidad de la CPU en GHz (un número)
- 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}GHzPor ejemplo, si las entradas son Intel, 3.5, y WorkStation, la salida debería ser:
Model: WorkStation
CPU: Intel @ 3.5GHzObserva 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
endCreación y uso de objetos compuestos:
local myEngine = Engine:new(200)
local myCar = Car:new("Sedan", myEngine)
print(myCar.engine.horsepower) -- 200La 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
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