Menu
Coddy logo textTech

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.

challenge icon

Aufgabe

Einfach

Lassen 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 eating ausgibt. Dies repräsentiert die Kernidentität, die alle Tiere teilen.
  • Swimmable.lua: Erstellen Sie ein Mixin mit einer :swim()-Methode, die {self.name} is swimming ausgibt. 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 Hilfsfunktion applyMixin. Lesen Sie Eingaben für den Namen eines Vogels und ob er schwimmen kann (yes oder no). 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:

  1. Der Name des Vogels (ein String)
  2. Ob der Vogel schwimmen kann: yes oder no

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

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

Wenn 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 swimming

Wenn 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
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 Object Oriented Programming