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.
Piensa en arrancar un coche. No metes la mano bajo el capó para hacer girar manualmente el motor. En su lugar, giras la llave y el coche se encarga internamente del resto.
En el código, esto significa que la clase Car proporciona un método sencillo :start() 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 indica al motor que haga su trabajo. Esto mantiene las responsabilidades separadas. La clase Engine se encarga de 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 clara para los usuarios de tu clase. Interactúan con el coche, no con sus partes internas. Si más adelante sustituyes el motor por una implementación diferente, la interfaz pública del coche permanece igual: solo cambia la delegación interna.
Desafío
Fácil¡Construyamos un sistema de reproductor de música que demuestre la delegación! Crearás un componente Speaker y un MusicPlayer que lo contiene. El reproductor no gestionará directamente la salida de sonido; 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 después delega en el método:playSound()del altavoz:stop(): imprime{brand} player stopped
main.lua: ¡Une todos los elementos! Lee las entradas correspondientes al volumen del altavoz, la marca del reproductor y el nombre de una pista. Crea primero un Speaker y después pásalo al constructor de MusicPlayer. Llama al método:play()del reproductor con el nombre de la pista y, después, 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 que se reproducirá (una cadena)
Tu salida debe 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 debe ser:
SoundMax player starting...
Playing "Summer Vibes" at volume 75
SoundMax player stoppedObserva cómo MusicPlayer no sabe cómo producir sonido. Simplemente le indica a su altavoz que se encargue de esa parte. Esto es la delegación en acción: el objeto externo proporciona una interfaz sencilla, mientras que el componente interno realiza el trabajo especializado.
Pruébalo tú mismo
-- main.lua
-- ¡Reúne 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: Crea un Speaker con el volume dado
-- TODO: Crea un MusicPlayer con el brand y el speaker
-- TODO: Llama al método play del player con el track name
-- TODO: Llama al método stop del player
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