Duck Typing
Teil des Abschnitts Object Oriented Programming der Lua-Journey von Coddy — Lektion 39 von 70.
Lua kümmert sich nicht um die Klasse eines Objekts – es zählt nur, was das Objekt tun kann. Dieses Konzept wird als Duck Typing bezeichnet: „Wenn es wie eine Ente watschelt und wie eine Ente quakt, dann ist es eine Ente.“ Programmiertechnisch gesehen: Wenn ein Objekt die Methode besitzt, die Sie aufrufen, dann funktioniert es.
Das bedeutet, dass völlig voneinander unabhängige Klassen zusammen verwendet werden können, solange sie denselben Methodennamen teilen:
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")
endDiese Klassen haben keine Vererbungsbeziehung – sie sind völlig unabhängig. Dennoch können wir sie identisch behandeln:
local shapes = {Circle:new(), Square:new(), Circle:new()}
for _, shape in ipairs(shapes) do
shape:draw()
end
-- Ausgabe:
-- Zeichne einen Kreis
-- Zeichne ein Quadrat
-- Zeichne einen KreisDie Schleife prüft nicht, welchen Typ jedes Objekt hat. Sie ruft einfach :draw() auf und vertraut darauf, dass die Methode existiert. Diese Flexibilität ist leistungsstark – Sie können neue Formtypen hinzufügen, ohne den Schleifencode überhaupt zu ändern. Solange die neue Klasse :draw() implementiert, funktioniert es einfach.
Aufgabe
EinfachLassen Sie uns die Stärke von Duck Typing demonstrieren, indem wir ein Benachrichtigungssystem mit völlig unabhängigen Klassen aufbauen, die alle auf die gleiche Weise verarbeitet werden können!
Sie werden drei unabhängige Benachrichtigungsklassen erstellen – keine davon erbt von einer anderen –, aber alle implementieren eine :send()-Methode. Das bedeutet, dass Sie eine gemischte Sammlung durchlaufen und :send() für jedes Element aufrufen können, ohne sich um dessen tatsächlichen Typ zu kümmern.
Sie werden Ihren Code in vier Dateien organisieren:
Email.lua: Eine Klasse mit einem:new(recipient)-Konstruktor, der speichert, an wen die E-Mail geht. Ihre:send()-Methode sollteEmailing {recipient}ausgeben.SMS.lua: Eine völlig separate Klasse mit einem:new(phoneNumber)-Konstruktor. Ihre:send()-Methode sollteTexting {phoneNumber}ausgeben.PushNotification.lua: Eine weitere unabhängige Klasse mit einem:new(deviceId)-Konstruktor. Ihre:send()-Methode solltePushing to {deviceId}ausgeben.main.lua: Bringen Sie alles zusammen! Lesen Sie drei Eingaben ein, erstellen Sie eine Instanz jedes Benachrichtigungstyps, legen Sie alle in eine einzige Tabelle, durchlaufen Sie diese dann in einer Schleife und rufen Sie für jede:send()auf.
Sie erhalten drei Eingaben:
- Einen E-Mail-Empfänger (z. B.
alice@example.com) - Eine Telefonnummer (z. B.
555-1234) - Eine Geräte-ID (z. B.
device_99)
Erstellen Sie in Ihrer Hauptdatei alle drei Benachrichtigungsobjekte, speichern Sie diese in einer Tabelle und iterieren Sie durch die Tabelle, wobei Sie für jedes Objekt :send() aufrufen. Die Reihenfolge sollte sein: Zuerst Email, dann SMS, dann PushNotification.
Wenn die Eingaben beispielsweise bob@mail.com, 555-9876 und phone_42 sind, sollte die Ausgabe wie folgt aussehen:
Emailing bob@mail.com
Texting 555-9876
Pushing to phone_42Beachten Sie, dass Ihre Schleife nicht prüfen muss, welchen Typ jedes Objekt hat – sie vertraut einfach darauf, dass jedes Objekt in der Sammlung eine :send()-Methode besitzt. Das ist Duck Typing in Aktion! Wenn es :send() kann, ist es eine Benachrichtigung.
Spickzettel
Lua verwendet Duck-Typing: Wenn ein Objekt die Methode besitzt, die Sie aufrufen, funktioniert es, unabhängig von seiner Klasse. Das Prinzip lautet: „Wenn es wie eine Ente watschelt und wie eine Ente quakt, dann ist es eine Ente.“
Völlig voneinander unabhängige Klassen können austauschbar verwendet werden, solange sie denselben Methodennamen teilen:
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")
endDiese Klassen haben keine Vererbungsbeziehung, können aber dennoch identisch behandelt werden:
local shapes = {Circle:new(), Square:new(), Circle:new()}
for _, shape in ipairs(shapes) do
shape:draw()
end
-- Ausgabe:
-- Drawing a circle
-- Drawing a square
-- Drawing a circleDie Schleife prüft keine Objekttypen – sie ruft einfach die Methode auf und vertraut darauf, dass sie existiert. Sie können neue Typen hinzufügen, ohne bestehenden Code zu ändern, solange diese die erforderliche Methode implementieren.
Probier es selbst
-- main.lua
-- Alle Benachrichtigungstypen zusammenführen und Duck-Typing demonstrieren
-- Die Benachrichtigungsklassen importieren
local Email = require('Email')
local SMS = require('SMS')
local PushNotification = require('PushNotification')
-- Eingaben lesen
local recipient = io.read()
local phoneNumber = io.read()
local deviceId = io.read()
-- TODO: Jeweils eine Instanz jedes Benachrichtigungstyps erstellen
-- TODO: Alle Benachrichtigungen in einer einzigen Tabelle speichern (zuerst Email, dann SMS, dann PushNotification)
-- TODO: Die Tabelle durchlaufen und :send() für jede Benachrichtigung aufrufen
-- Dies demonstriert Duck-Typing - wir prüfen nicht den Typ, sondern rufen einfach :send() auf
Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.
Alle Lektionen in Object Oriented Programming
1Das 'self'-Konzept
Tabellen mit FunktionenExplizites 'self'Die Doppelpunkt-SyntaxPunkt vs. DoppelpunktZusammenfassung – Beweglicher Punkt4Projekt: Digitale Bank
Projekt-SetupEinzahlungsmethode7Polymorphie & Überschreiben
Methoden überschreibenElternmethoden aufrufenDuck TypingGemeinsames InterfaceTyp prüfenRückblick - Mitarbeiterrollen10Projekt: Formen-Manager
Projekt-SetupRechteck-Klasse2Klassen-Prototyp-Muster
Das Prototyp-KonzeptVerknüpfung mit __indexDer :new()-KonstruktorAttribute initialisierenUnabhängige InstanzenZusammenfassung – Autofabrik5Operatorüberladung in OOP
Objekte addierenObjekte subtrahierenObjekte verkettenObjekte vergleichen (<, >)Rückblick – Wallet-Mathematik3Objektzustand und -verhalten
InstanzvariablenGetter-MethodenSetter-MethodenBerechnete EigenschaftenStrings formatierenGleichheitsprüfungenZusammenfassung – Studentennote6Grundlagen der Vererbung
Das Vererbungs-SetupMethoden vererbenDen Konstruktor erweiternChild-Methoden hinzufügenGemeinsam vs. EinzigartigZusammenfassung - Formen-Hierarchie9Komposition & Mixins
Has-A-BeziehungDelegationEinfache MixinsAnwendung mehrerer MixinsMixins vs. VererbungZusammenfassung – Roboter-Montage12Abschluss-Herausforderungen
Wiederholung - InventarsystemWiederholung - BenutzerverwaltungWiederholung - Score-TrackerFinaler OOP-Check