Duck Typing
Parte de la sección Programación Orientada a Objetos del Journey de Lua de Coddy. Lección 39 de 70.
A Lua no le importa la clase de un objeto. Solo le importa lo que el objeto puede hacer. Este concepto se llama tipado de pato: «Si camina como un pato y grazna como un pato, es un pato». En términos de programación, si un objeto tiene el método que estás llamando, funciona.
Esto significa que clases completamente no relacionadas pueden usarse juntas, siempre que compartan el mismo nombre de método:
local Circle = {}
Circle.__index = Circle
function Circle:new()
local obj = {}
setmetatable(obj, Circle)
return obj
end
function Circle:draw()
print("Drawing a circle")
end
local Square = {}
Square.__index = Square
function Square:new()
local obj = {}
setmetatable(obj, Square)
return obj
end
function Square:draw()
print("Drawing a square")
endEstas clases no tienen ninguna relación de herencia: son completamente independientes. Sin embargo, podemos tratarlas de forma idéntica:
local shapes = {Circle:new(), Square:new(), Circle:new()}
for _, shape in ipairs(shapes) do
shape:draw()
end
-- Salida:
-- Dibujando un círculo
-- Dibujando un cuadrado
-- Dibujando un círculoEl bucle no comprueba de qué tipo es cada objeto. Simplemente llama a :draw() y confía en que el método exista. Esta flexibilidad es potente. Puedes añadir nuevos tipos de formas sin modificar el código del bucle en absoluto. Siempre que la nueva clase implemente :draw(), funciona sin más.
Desafío
Fácil¡Demostremos el poder del tipado pato creando un sistema de notificaciones con clases completamente independientes que se puedan procesar de la misma manera!
Crearás tres clases de notificación independientes; ninguna hereda de las demás, pero todas implementan un método :send(). Esto significa que puedes recorrer una colección mixta y llamar a :send() en cada elemento sin preocuparte por su tipo real.
Organizarás tu código en cuatro archivos:
Email.lua: Una clase con un constructor:new(recipient)que almacena el destinatario del correo electrónico. Su método:send()debe imprimirEmailing {recipient}.SMS.lua: Una clase completamente independiente con un constructor:new(phoneNumber). Su método:send()debe imprimirTexting {phoneNumber}.PushNotification.lua: Otra clase independiente con un constructor:new(deviceId). Su método:send()debe imprimirPushing to {deviceId}.main.lua: ¡Une todo! Lee tres entradas, crea una instancia de cada tipo de notificación, colócalas todas en una sola tabla, después recórrela y llama a:send()en cada elemento.
Recibirás tres entradas:
- Un destinatario de correo electrónico (por ejemplo,
alice@example.com) - Un número de teléfono (por ejemplo,
555-1234) - Un ID de dispositivo (por ejemplo,
device_99)
En tu archivo principal, crea los tres objetos de notificación, almacénalos en una tabla e itera por la tabla llamando a :send() en cada uno. El orden debe ser: Email primero, después SMS y, por último, PushNotification.
Por ejemplo, si las entradas son bob@mail.com, 555-9876 y phone_42, la salida debería ser:
Emailing bob@mail.com
Texting 555-9876
Pushing to phone_42Observa que tu bucle no necesita comprobar de qué tipo es cada objeto. Simplemente confía en que todos los objetos de la colección tienen un método :send(). ¡Eso es el tipado pato en acción! Si puede hacer :send(), es una notificación.
Pruébalo tú mismo
-- main.lua
-- Reúne todos los tipos de notificación y demuestra el duck typing
-- Importa las clases de notificación
local Email = require('Email')
local SMS = require('SMS')
local PushNotification = require('PushNotification')
-- Lee las entradas
local recipient = io.read()
local phoneNumber = io.read()
local deviceId = io.read()
-- TODO: Crea una instancia de cada tipo de notificación
-- TODO: Pon todas las notificaciones en una sola tabla (Email primero, luego SMS, luego PushNotification)
-- TODO: Recorre la tabla y llama a :send() en cada notificación
-- Esto demuestra el duck typing - no comprobamos el tipo, solo llamamos a :send()
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ósito7Polimorfismo y Sobrescritura
Sobrescritura de métodosLlamar a métodos del padreDuck TypingInterfaz comúnComprobación de tiposRepaso - Roles de empleados2Patró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 formasPractica por tu cuenta: Compilador de Lua online