Mixins vs. Vererbung
Teil des Abschnitts Object Oriented Programming der Lua-Journey von Coddy — Lektion 53 von 70.
Sie haben zwei Möglichkeiten kennengelernt, Verhalten zu teilen: Vererbung erzeugt eine „is-a“-Beziehung über die Prototypenkette, während Mixins Funktionen direkt in eine Klasse kopieren. Aber wann sollten Sie welchen Ansatz verwenden?
Verwenden Sie Vererbung, wenn Objekte eine grundlegende Identität teilen. Ein Bird ist ein Animal – es ist sinnvoll, dass Bird grundlegende Verhaltensweisen von Tieren wie :eat() oder :sleep() erbt. Die Beziehung ist dauerhaft und definiert, was das Objekt im Kern ist.
Verwenden Sie Mixins, wenn Sie eine Funktion hinzufügen möchten, die nicht die Kernidentität des Objekts definiert.
Nicht alle Vögel können fliegen – Pinguine und Strauße sind Vögel, aber sie fliegen nicht. Fliegen ist eine Fähigkeit, kein definierendes Merkmal eines Vogels. Ein Flyable-Mixin ermöglicht es Ihnen, Vögeln, die es benötigen, die Flugfähigkeit hinzuzufügen, ohne sie allen aufzuzwingen.
Die beiden Ansätze arbeiten wunderbar zusammen:
-- Bird erbt von Animal (is-a Beziehung)
local Bird = {}
Bird.__index = Bird
setmetatable(Bird, {__index = Animal})
-- Flyable ist eine Fähigkeit (Mixin)
local Flyable = {}
function Flyable:fly()
print(self.name .. " soars through the sky!")
end
-- Wende das Mixin nur auf Vögel an, die fliegen können
applyMixin(Bird, Flyable)Dieses Muster bietet Ihnen das Beste aus beiden Welten: eine klare Klassenhierarchie für eine gemeinsame Identität sowie flexible Funktionen, die bei Bedarf beigemischt werden können.
Aufgabe
EinfachLassen Sie uns ein kleines Ökosystem von Lebewesen aufbauen, das zeigt, wann man Vererbung gegenüber Mixins verwendet! Sie erstellen eine Animal-Basisklasse, einen Bird, der davon erbt, und ein Swimmable-Mixin – denn während alle Vögel Tiere sind, können nicht alle Vögel schwimmen.
Sie werden Ihren Code auf vier Dateien verteilen:
Animal.lua: Erstellen Sie die Basisklasse Animal mit einem Konstruktor:new(name), der den Namen speichert. Fügen Sie eine:eat()-Methode hinzu, die{name} is eatingausgibt. Dies repräsentiert die Kernidentität, die alle Tiere teilen.Swimmable.lua: Erstellen Sie ein Mixin mit einer:swim()-Methode, die{self.name} is swimmingausgibt. Dies ist eine Fähigkeit – nicht alle Lebewesen können schwimmen, daher sollte sie nicht Teil der Vererbungskette sein.Bird.lua: Erstellen Sie eine Bird-Klasse, die von Animal erbt. Der Konstruktor:new(name, canSwim)sollte den Konstruktor der Elternklasse aufrufen und speichern, ob dieser Vogel schwimmen kann. Wenden Sie das Swimmable-Mixin auf die Bird-Klasse an. Fügen Sie eine:chirp()-Methode hinzu, die{name} chirps!ausgibt – dies ist ein vogelspezifisches Verhalten.main.lua: Schreiben Sie hier die HilfsfunktionapplyMixin. Lesen Sie Eingaben für den Namen eines Vogels und ob er schwimmen kann (yesoderno). Erstellen Sie eine Bird-Instanz und demonstrieren Sie dann ihre Fähigkeiten: Rufen Sie:eat()auf (von Animal geerbt), rufen Sie:chirp()auf (Birds eigene Methode) und rufen Sie:swim()nur auf, wenn der Vogel schwimmen kann.
Sie erhalten zwei Eingaben:
- Der Name des Vogels (ein String)
- Ob der Vogel schwimmen kann:
yesoderno
Wenn der Vogel schwimmen kann, sollte Ihre Ausgabe wie folgt aussehen:
{name} is eating
{name} chirps!
{name} is swimmingWenn der Vogel nicht schwimmen kann, sollte Ihre Ausgabe wie folgt aussehen:
{name} is eating
{name} chirps!Wenn die Eingaben beispielsweise Duck und yes sind, sollte die Ausgabe wie folgt aussehen:
Duck is eating
Duck chirps!
Duck is swimmingWenn die Eingaben Sparrow und no sind, sollte die Ausgabe wie folgt aussehen:
Sparrow is eating
Sparrow chirps!Diese Herausforderung zeigt den entscheidenden Unterschied: Ein Bird ist ein Animal (Vererbung), aber Schwimmen ist eine optionale Fähigkeit (Mixin). Das Mixin wird auf die Klasse angewendet, aber Ihre Hauptlogik entscheidet basierend auf den Fähigkeiten des einzelnen Vogels, ob es verwendet wird!
Spickzettel
Verwenden Sie Vererbung, wenn Objekte eine grundlegende Identität teilen (eine „ist-ein“-Beziehung). Verwenden Sie Mixins, wenn Sie Funktionen hinzufügen, die nicht die Kernidentität des Objekts definieren.
Vererbung schafft eine dauerhafte Beziehung über die Prototypenkette:
local Bird = {}
Bird.__index = Bird
setmetatable(Bird, {__index = Animal})Mixins fügen optionale Funktionen hinzu, die selektiv angewendet werden können:
local Flyable = {}
function Flyable:fly()
print(self.name .. " soars through the sky!")
end
applyMixin(Bird, Flyable)Die beiden Ansätze arbeiten zusammen: Verwenden Sie Vererbung für eine gemeinsame Identität und Mixins für flexible Funktionen, die nur einige Instanzen benötigen.
Probier es selbst
-- main.lua: Einstiegspunkt - erstelle einen Vogel und demonstriere seine Fähigkeiten
-- Hilfsfunktion, um ein Mixin auf eine Klasse anzuwenden
local function applyMixin(class, mixin)
-- TODO: Kopiere alle Methoden vom Mixin in die Klasse
end
-- Wende das Swimmable-Mixin auf Bird an, bevor Bird angefordert wird
-- Hinweis: Dies muss eventuell in Bird.lua oder hier behandelt werden
local Bird = require('Bird')
local Swimmable = require('Swimmable')
-- Mixin auf die Bird-Klasse anwenden
applyMixin(Bird, Swimmable)
-- Eingaben lesen
local name = io.read()
local canSwimInput = io.read()
-- TODO: Bestimme basierend auf der Eingabe ("yes" oder "no"), ob der Vogel schwimmen kann
-- TODO: Erstelle eine Bird-Instanz mit dem Namen und der Schwimmfähigkeit
-- TODO: Rufe eat() auf - von Animal geerbt
-- TODO: Rufe chirp() auf - Birds eigene Methode
-- TODO: Rufe swim() nur auf, wenn der Vogel schwimmen kann
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