Duck Typing
Fa parte della sezione Programmazione orientata agli oggetti del percorso Lua di Coddy. Lezione 39 di 70.
A Lua non interessa la classe di un oggetto. Le importa solo ciò che l'oggetto può fare. Questo concetto si chiama duck typing: «Se cammina come un'anatra e starnazza come un'anatra, è un'anatra». In termini di programmazione, se un oggetto ha il metodo che stai chiamando, funziona.
Ciò significa che classi completamente non correlate possono essere usate insieme, purché condividano lo stesso nome di metodo:
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")
endQueste classi non hanno alcuna relazione di ereditarietà: sono completamente indipendenti. Eppure possiamo trattarle in modo identico:
local shapes = {Circle:new(), Square:new(), Circle:new()}
for _, shape in ipairs(shapes) do
shape:draw()
end
-- Output:
-- Disegno di un cerchio
-- Disegno di un quadrato
-- Disegno di un cerchioIl ciclo non controlla di che tipo è ogni oggetto. Chiama semplicemente :draw() e presume che il metodo esista. Questa flessibilità è potente. Puoi aggiungere nuovi tipi di forme senza modificare affatto il codice del ciclo. Finché la nuova classe implementa :draw(), funziona e basta.
Sfida
FacileDimostriamo la potenza del duck typing creando un sistema di notifiche con classi completamente indipendenti, che possono essere elaborate tutte allo stesso modo!
Creerai tre classi di notifiche indipendenti, nessuna delle quali eredita dalle altre, ma tutte implementano un metodo :send(). Questo significa che puoi scorrere una raccolta mista e chiamare :send() su ciascun elemento senza preoccuparti del suo tipo effettivo.
Organizzerai il codice in quattro file:
Email.lua: una classe con un costruttore:new(recipient)che memorizza il destinatario dell'email. Il suo metodo:send()dovrebbe stampareEmailing {recipient}.SMS.lua: una classe completamente separata con un costruttore:new(phoneNumber). Il suo metodo:send()dovrebbe stampareTexting {phoneNumber}.PushNotification.lua: un'altra classe indipendente con un costruttore:new(deviceId). Il suo metodo:send()dovrebbe stamparePushing to {deviceId}.main.lua: metti insieme ogni elemento! Leggi tre input, crea un'istanza di ogni tipo di notifica, inseriscile tutte in un'unica tabella, quindi scorri la tabella e chiama:send()su ciascuna.
Riceverai tre input:
- Un destinatario email (ad es.,
alice@example.com) - Un numero di telefono (ad es.,
555-1234) - L'ID di un dispositivo (ad es.,
device_99)
Nel file principale, crea tutti e tre gli oggetti di notifica, memorizzali in una tabella e scorri la tabella chiamando :send() su ciascuno. L'ordine dovrebbe essere: prima Email, poi SMS, infine PushNotification.
Ad esempio, se gli input sono bob@mail.com, 555-9876 e phone_42, l'output dovrebbe essere:
Emailing bob@mail.com
Texting 555-9876
Pushing to phone_42Nota che il ciclo non deve verificare il tipo di ciascun oggetto. Si fida semplicemente del fatto che ogni oggetto nella raccolta abbia un metodo :send(). Questo è il duck typing in azione! Se può eseguire :send(), è una notifica.
Provalo tu
-- main.lua
-- Riunisci tutti i tipi di notifica e dimostra il duck typing
-- Importa le classi delle notifiche
local Email = require('Email')
local SMS = require('SMS')
local PushNotification = require('PushNotification')
-- Leggi gli input
local recipient = io.read()
local phoneNumber = io.read()
local deviceId = io.read()
-- TODO: Crea un'istanza di ogni tipo di notifica
-- TODO: Inserisci tutte le notifiche in un'unica tabella (prima Email, poi SMS, infine PushNotification)
-- TODO: Scorri la tabella e chiama :send() su ogni notifica
-- Questo dimostra il duck typing: non controlliamo il tipo, chiamiamo semplicemente :send()
Questa lezione include un breve quiz. Inizia la lezione per rispondere e tenere traccia dei tuoi progressi.
Tutte le lezioni di Programmazione orientata agli oggetti
1Il concetto di «self»
Tabelle con funzioni«self» esplicitoLa sintassi dei due puntiPunto o due puntiRiepilogo - Spostare il punto4Progetto: Banca digitale
Configurazione del progettoMetodo di deposito7Polimorfismo e sovrascrittura
Sovrascrivere i metodiChiamare i metodi della classe genitoreDuck TypingInterfaccia comuneVerificare il tipoRiepilogo - Ruoli dei dipendenti2Schema del prototipo di classe
Il concetto di prototipoCollegare con __indexIl costruttore :new()Inizializzare gli attributiIstanze indipendentiRiepilogo - Fabbrica di auto5Sovraccarico degli operatori nella OOP
Sommare oggettiSottrarre oggettiConcatenare oggettiConfrontare oggetti (<, >)Ripasso - Calcoli con il portafoglio8Incapsulamento
Convenzioni di denominazioneClosure per la privacyAccesso tramite closureTabelle di sola letturaLogica di convalidaRiepilogo - Cassaforte sicura11Modelli di progettazione (base)
Funzioni factoryTabella singletonPattern IteratorObserver (listener)Riepilogo - Factory per logger3Stato e comportamento degli oggetti
Variabili di istanzaMetodi getterMetodi setterProprietà calcolateFormattazione delle stringheControlli di uguaglianzaRiepilogo - Voto dello studente6Fondamenti dell'ereditarietà
Configurare l'ereditarietàEreditare i metodiEstendere il costruttoreAggiungere metodi alla classe figliaCondiviso vs specificoRiepilogo - Gerarchia delle formeEsercitati da solo: Compilatore Lua online