Menu
CoddyTech

Mixins vs. Vererbung

Teil des Abschnitts Objektorientierte Programmierung der Lua-Journey von Coddy. Lektion 53 von 70.

Du hast zwei Möglichkeiten kennengelernt, Verhalten zu teilen: Vererbung schafft über die Prototypenkette eine „Ist-ein“-Beziehung, während Mixins Funktionen direkt in eine Klasse kopieren. Aber wann solltest du welchen Ansatz verwenden?

Verwende 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 Grunde ist.

Verwende Mixins, wenn du eine Fähigkeit hinzufügen möchtest, die nicht die grundlegende Identitä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 dir, Vögeln, die es benötigen, das Fliegen hinzuzufügen, ohne es allen aufzuzwingen.

Die beiden Ansätze ergänzen sich. Hier erben sowohl Eagle als auch Penguin von Bird, und nur Eagle erhält das Mixin:

-- Bird erbt von Animal (Ist-ein-Beziehung)
local Bird = {}
Bird.__index = Bird
setmetatable(Bird, {__index = Animal})

-- Eagle und Penguin sind beide Vögel
local Eagle = setmetatable({}, {__index = Bird})
Eagle.__index = Eagle
local Penguin = setmetatable({}, {__index = Bird})
Penguin.__index = Penguin

-- Flyable ist eine Fähigkeit (Mixin)
local Flyable = {}
function Flyable:fly()
    print(self.name .. " soars through the sky!")
end

-- Wende das Mixin nur auf die Vögel an, die fliegen
applyMixin(Eagle, Flyable)

Ein Adler kann :fly() aufrufen. Ein Pinguin kann das nicht: fly ist weder in Penguin noch in Bird oder Animal enthalten, daher ist penguin:fly() ein Fehler. Würde man das Mixin stattdessen auf Bird anwenden, würde fly in jeden Vogel kopiert, einschließlich der Pinguine.

challenge icon

Aufgabe

Einfach

Lass uns ein kleines Ökosystem aus Lebewesen aufbauen, das zeigt, wann Vererbung und wann Mixins sinnvoll sind! Du erstellst eine Basisklasse Animal, eine davon abgeleitete Klasse Bird und ein Mixin Swimmable, denn obwohl alle Vögel Tiere sind, können nicht alle Vögel schwimmen.

Du verteilst deinen Code auf vier Dateien:

  • Animal.lua: Erstelle die Basisklasse Animal mit einem Konstruktor :new(name), der den Namen speichert. Füge eine Methode :eat() hinzu, die {name} is eating ausgibt. Dies stellt die gemeinsame grundlegende Identität aller Tiere dar.
  • Swimmable.lua: Erstelle ein Mixin mit einer Methode :swim(), die {self.name} is swimming ausgibt. Dies ist eine Fähigkeit. Da nicht alle Lebewesen schwimmen können, sollte sie nicht Teil der Vererbungskette sein.
  • Bird.lua: Erstelle eine Klasse Bird, die von Animal erbt. Der Konstruktor :new(name) sollte den Konstruktor der Elternklasse aufrufen. Füge eine Methode :chirp() hinzu, die {name} chirps! ausgibt. Dies ist ein vogelspezifisches Verhalten. Füge das Schwimmen nicht zu Bird hinzu: Nicht jeder Vogel kann schwimmen.
  • main.lua: Schreibe hier die Hilfsfunktion applyMixin(target, mixin): Sie kopiert jede Funktion des Mixins in das Ziel. Lies den Namen des Vogels und ob er schwimmen kann (yes oder no) ein. Erstelle einen Bird und wende das Swimmable-Mixin nur dann auf diesen Vogel an, wenn er schwimmen kann. Rufe anschließend :eat() (von Animal geerbt) und :chirp() (eigene Methode von Bird) auf und rufe :swim() nur dann auf, wenn dieser Vogel die Methode hat (if bird.swim then).

Du erhältst zwei Eingaben:

  1. Den Namen des Vogels (eine Zeichenkette)
  2. Ob der Vogel schwimmen kann: yes oder no

Wenn der Vogel schwimmen kann, sollte deine Ausgabe wie folgt aussehen:

{name} is eating
{name} chirps!
{name} is swimming

Wenn der Vogel nicht schwimmen kann, sollte deine Ausgabe wie folgt aussehen:

{name} is eating
{name} chirps!

Wenn die Eingaben beispielsweise Duck und yes lauten, sollte die Ausgabe wie folgt aussehen:

Duck is eating
Duck chirps!
Duck is swimming

Wenn die Eingaben Sparrow und no lauten, sollte die Ausgabe wie folgt aussehen:

Sparrow is eating
Sparrow chirps!

Diese Herausforderung zeigt den entscheidenden Unterschied: Bird ist ein Animal (Vererbung), während Schwimmen eine optionale Fähigkeit ist (Mixin). Da das Mixin nur den schwimmenden Vögeln hinzugefügt wird, hat ein Vogel, der nicht schwimmen kann, überhaupt keine swim-Methode.

Probier es selbst

-- main.lua: Einstiegspunkt – einen Vogel erstellen und seine Fähigkeiten demonstrieren

-- Hilfsfunktion, um ein Mixin auf eine Klasse oder ein einzelnes Objekt anzuwenden
local function applyMixin(target, mixin)
    -- TODO: Jede Funktion aus mixin nach target kopieren

end

local Bird = require('Bird')
local Swimmable = require('Swimmable')

-- Eingaben lesen
local name = io.read()
local canSwimInput = io.read()

-- TODO: Eine Bird-Instanz mit dem Namen erstellen

-- TODO: Wenn die Eingabe "yes" ist, das Swimmable-Mixin nur auf diesen Vogel anwenden

-- TODO: eat() aufrufen – von Animal geerbt

-- TODO: chirp() aufrufen – Birds eigene Methode

-- TODO: swim() nur aufrufen, wenn dieser Vogel die Methode besitzt
quiz iconTeste dich selbst

Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.

Alle Lektionen in Objektorientierte Programmierung

Übe selbstständig: Online-Lua-Compiler