Delegación
Parte de la sección Object Oriented Programming del Journey de Lua de Coddy — lección 50 de 70.
Almacenar un objeto dentro de otro objeto es solo el primer paso. El verdadero poder de la composición proviene de la delegación: cuando el objeto externo reenvía las llamadas a métodos a sus componentes internos.
Piense en arrancar un coche. No mete la mano bajo el capó y hace girar el motor manualmente. En su lugar, gira la llave y el coche se encarga del resto internamente.
En el código, esto significa que la clase Car proporciona un método :start() simple que delega el trabajo real al motor:
function Car:start()
print("Turning the key...")
self.engine:run() -- Delegar al objeto interno
endEl coche no necesita saber cómo funciona el motor; simplemente le dice al motor que haga su trabajo. Esto mantiene las responsabilidades separadas. La clase Engine maneja la lógica del motor, mientras que Car coordina las piezas:
function Engine:run()
print("Engine running at " .. self.horsepower .. " HP")
end
local engine = Engine:new(250)
local car = Car:new("Coupe", engine)
car:start()
-- Salida:
-- Girando la llave...
-- Motor funcionando a 250 HPLa delegación crea una interfaz limpia para los usuarios de su clase. Interactúan con el coche, no con sus partes internas. Si más adelante cambia a una implementación de motor diferente, la interfaz pública del coche permanece igual; solo cambia la delegación interna.
Desafío
Fácil¡Vamos a construir un sistema de reproductor de música que demuestre la delegación! Crearás un componente Speaker y un MusicPlayer que lo contenga. El reproductor no gestionará la salida de sonido directamente; en su lugar, delegará esa responsabilidad a su altavoz interno.
Organizarás tu código en tres archivos:
Speaker.lua: Crea una clase Speaker con un constructor:new(volume)que almacene el nivel de volumen. Incluye un método:playSound(trackName)que imprima:Playing "{trackName}" at volume {volume}MusicPlayer.lua: Crea una clase MusicPlayer cuyo constructor:new(brand, speaker)acepte un nombre de marca y un objeto Speaker. El reproductor almacena el altavoz en su interior. Incluye estos métodos::play(trackName)— imprime{brand} player starting...y luego delega al método:playSound()del altavoz:stop()— imprime{brand} player stopped
main.lua: ¡Une todo! Lee las entradas para el volumen del altavoz, la marca del reproductor y un nombre de pista. Crea primero un Speaker, luego pásalo al constructor de MusicPlayer. Llama al método:play()del reproductor con el nombre de la pista, luego llama a:stop().
Recibirás tres entradas:
- El volumen del altavoz (un número)
- La marca del reproductor de música (una cadena)
- El nombre de la pista a reproducir (una cadena)
Tu salida debería mostrar al reproductor coordinándose con su altavoz:
{brand} player starting...
Playing "{trackName}" at volume {volume}
{brand} player stoppedPor ejemplo, si las entradas son 75, SoundMax, y Summer Vibes, la salida debería ser:
SoundMax player starting...
Playing "Summer Vibes" at volume 75
SoundMax player stoppedObserva cómo el MusicPlayer no sabe cómo producir sonido; simplemente le dice a su altavoz que se encargue de esa parte. ¡Esto es la delegación en acción: el objeto externo proporciona una interfaz limpia mientras que el componente interno realiza el trabajo especializado!
Hoja de referencia
La delegación permite que un objeto externo reenvíe llamadas a métodos a sus componentes internos, manteniendo las responsabilidades separadas y creando interfaces limpias.
El objeto externo proporciona métodos simples que delegan el trabajo a los objetos internos:
function Car:start()
print("Turning the key...")
self.engine:run() -- Delegate to the inner object
endEl objeto interno maneja su propia lógica especializada:
function Engine:run()
print("Engine running at " .. self.horsepower .. " HP")
end
local engine = Engine:new(250)
local car = Car:new("Coupe", engine)
car:start()
-- Output:
-- Turning the key...
-- Engine running at 250 HPBeneficios de la delegación:
- La clase externa no necesita saber cómo funciona el objeto interno
- Los usuarios interactúan con una interfaz pública limpia
- Las implementaciones internas se pueden intercambiar sin cambiar la interfaz pública
Pruébalo tú mismo
-- main.lua
-- ¡Juntando todo!
local Speaker = require('Speaker')
local MusicPlayer = require('MusicPlayer')
-- Leer entradas
local volume = tonumber(io.read())
local brand = io.read()
local trackName = io.read()
-- TODO: Crear un Speaker con el volumen dado
-- TODO: Crear un MusicPlayer con la marca y el altavoz
-- TODO: Llamar al método play del reproductor con el nombre de la pista
-- TODO: Llamar al método stop del reproductor
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