Den Konstruktor erweitern
Teil des Abschnitts Object Oriented Programming der Lua-Journey von Coddy — Lektion 33 von 70.
Wenn eine Kindklasse von einer Elternklasse erbt, möchte man oft, dass der Konstruktor der Kindklasse alles initialisiert, was auch die Elternklasse tun würde – plus alle zusätzlichen Felder, die spezifisch für das Kind sind. Anstatt Code zu duplizieren, können Sie den Konstruktor der Elternklasse direkt aufrufen.
Die Technik besteht darin, den Namen der Elternklasse zu verwenden, um deren :new()-Methode aufzurufen, wobei die Elternklasse als erstes Argument übergeben wird. Dies können Sie mit der Punkt-Syntax tun: Vehicle.new(Vehicle, speed), oder gleichwertig mit der Doppelpunkt-Syntax: Vehicle:new(speed) — beide sind gültig und führen zum gleichen Ergebnis. Dies erstellt das Basisobjekt mit der gesamten Initialisierung der Elternklasse, das Sie anschließend für die Kindklasse anpassen.
local Vehicle = {}
Vehicle.__index = Vehicle
function Vehicle:new(speed)
local obj = {speed = speed}
setmetatable(obj, Vehicle)
return obj
end
-- Car erbt von Vehicle
local Car = {}
Car.__index = Car
setmetatable(Car, {__index = Vehicle})
function Car:new(speed, brand)
local obj = Vehicle.new(Vehicle, speed) -- Eltern-Konstruktor aufrufen
obj.brand = brand -- Kind-spezifisches Feld hinzufügen
setmetatable(obj, Car) -- Erneut mit Car verknüpfen
return obj
endBeachten Sie die wichtigsten Schritte im Konstruktor der Kindklasse: Zuerst rufen wir Vehicle.new(Vehicle, speed) (oder gleichbedeutend Vehicle:new(speed)) auf, um ein Objekt zu erhalten, bei dem das speed-Feld bereits gesetzt ist. Dann fügen wir das für die Kindklasse spezifische brand-Feld hinzu. Schließlich ändern wir die Metatabelle von Vehicle zu Car, damit die Instanz Methoden zuerst in der Kindklasse nachschlägt.
local myCar = Car:new(120, "Toyota")
print(myCar.speed) -- Ausgabe: 120
print(myCar.brand) -- Ausgabe: ToyotaDieses Muster hält Ihren Code DRY – die gemeinsame Initialisierungslogik befindet sich im Parent, während jedes Child nur seine eindeutigen Attribute verarbeitet.
Aufgabe
EinfachLassen Sie uns eine Kreaturenhierarchie aufbauen, bei der eine Kindklasse den Konstruktor ihrer Elternklasse erweitert! Sie werden eine Elternklasse Animal und eine Kindklasse Bird erstellen, wobei der Konstruktor von Bird den Konstruktor von Animal aufruft, um die gemeinsame Initialisierung zu übernehmen, bevor er sein eigenes, einzigartiges Attribut hinzufügt.
Sie werden Ihren Code auf drei Dateien verteilen:
Animal.lua: Definieren Sie Ihre Elternklasse mit einem:new(name)-Konstruktor, der den Namen des Tieres speichert. Fügen Sie eine:describe()-Methode hinzu, die den Namen des Tieres ausgibt. Richten Sie das Standard-Klassenmuster mit__indexein und geben Sie die Klasse zurück.Bird.lua: Definieren Sie Ihre Kindklasse, die von Animal erbt. Der:new(name, wingspan)-Konstruktor Ihres Birds sollte den Elternkonstruktor mitAnimal.new(Animal, name)aufrufen, um den Namen zu initialisieren, dann das Feldwingspanzum Objekt hinzufügen und schließlich die Metatabelle wieder mit Bird verknüpfen. Fügen Sie eine:info()-Methode hinzu, die sowohl den Namen als auch die Flügelspannweite in einem bestimmten Format ausgibt.main.lua: Binden Sie Ihr Bird-Modul ein, lesen Sie zwei Eingaben (Name und Flügelspannweite), erstellen Sie eine Bird-Instanz und rufen Sie beide Methoden auf, um zu zeigen, dass geerbte und kindspezifische Funktionalität zusammenarbeiten.
Sie erhalten zwei Eingaben:
- Der Name des Vogels
- Die Flügelspannweite des Vogels (eine Zahl)
Erstellen Sie in Ihrer Hauptdatei einen Bird mit dem angegebenen Namen und der Flügelspannweite. Rufen Sie dann :describe() gefolgt von :info() in separaten Zeilen auf.
Die Methode :describe() (geerbt von Animal) sollte Folgendes ausgeben:
Animal: {name}Die Methode :info() (definiert in Bird) sollte Folgendes ausgeben:
{name} has a wingspan of {wingspan}Wenn die Eingaben beispielsweise Eagle und 200 sind, sollte die Ausgabe wie folgt aussehen:
Animal: Eagle
Eagle has a wingspan of 200Der Schlüssel hierbei ist, dass Ihr Bird-Konstruktor die Namensinitialisierung nicht dupliziert – er delegiert diese Arbeit an den Konstruktor von Animal und fügt dann nur das hinzu, was für Vögel einzigartig ist. Dies hält Ihren Code DRY, während eine ordnungsgemäße Vererbungskette aufgebaut wird!
Spickzettel
Um einen Konstruktor der Elternklasse aus einer Kindklasse aufzurufen, verwenden Sie den Namen der Elternklasse mit der Punkt-Syntax und übergeben Sie die Elternklasse selbst als erstes Argument:
local obj = ParentClass.new(ParentClass, parentArgs)Nachdem Sie den Eltern-Konstruktor aufgerufen haben, fügen Sie kindspezifische Felder hinzu und verknüpfen Sie die Metatabelle erneut mit der Kindklasse:
local Vehicle = {}
Vehicle.__index = Vehicle
function Vehicle:new(speed)
local obj = {speed = speed}
setmetatable(obj, Vehicle)
return obj
end
local Car = {}
Car.__index = Car
setmetatable(Car, {__index = Vehicle})
function Car:new(speed, brand)
local obj = Vehicle.new(Vehicle, speed) -- Eltern-Konstruktor aufrufen
obj.brand = brand -- Kindspezifisches Feld hinzufügen
setmetatable(obj, Car) -- Erneut mit Car verknüpfen
return obj
end
local myCar = Car:new(120, "Toyota")
print(myCar.speed) -- Ausgabe: 120
print(myCar.brand) -- Ausgabe: ToyotaDieses Muster hält die Initialisierungslogik DRY – das gemeinsame Setup befindet sich in der Elternklasse, während Kindklassen nur ihre einzigartigen Attribute verarbeiten.
Probier es selbst
-- main.lua: Einstiegspunkt
local Bird = require('Bird')
-- Eingaben lesen
local name = io.read()
local wingspan = tonumber(io.read())
-- TODO: Erstelle eine Bird-Instanz mit dem angegebenen Namen und der Flügelspannweite
-- TODO: Rufe :describe() für den Vogel auf (geerbt von Animal)
-- TODO: Rufe :info() für den Vogel auf (definiert in Bird)
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