Delegación
Parte de la sección Programación Orientada a Objetos 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!
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 Programación Orientada a Objetos
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 robotsPractica por tu cuenta: Compilador de Lua online