Komposition vs. Vererbung
Teil des Abschnitts Objektorientierte Programmierung der GDScript-Journey von Coddy. Lektion 40 von 65.
Klassen verwenden Code auf zwei Arten wieder. Vererbung (extends) bedeutet, dass eine Klasse eine Art einer anderen Klasse ist: Ein Mage ist ein Player. Komposition bedeutet, dass eine Klasse einen Bestandteil hat: Sie speichert ein anderes Objekt in einer Member-Variable und überträgt die Arbeit an dieses:
# motor.gd
class_name Motor
extends RefCounted
func start() -> String:
return "motor running"
# car.gd
class_name Car
extends RefCounted
var motor := Motor.new()
func start() -> String:
return "Car starts, " + motor.start()Car.new().start() gibt Car starts, motor running zurück. Das Auto weiß nicht, wie ein Motor funktioniert: Es ruft motor.start() auf. Einen Aufruf wie diesen an einen Bestandteil weiterzugeben, nennt man Delegation. (Die Klasse heißt Motor, weil Engine bereits eine Godot-Klasse ist.)
Komposition zahlt sich aus, wenn sich Teile ändern oder kombinieren. Bei Vererbung sind eine gehende Figur und eine fliegende Figur zwei Unterklassen, und eine Fledermaus, die zu fliegen beginnt, kann ihre Klasse nicht ändern. Bei Komposition hat die Figur ein Bewegungsteil, und es auszutauschen, ist eine einzige Zuweisung:
# character.gd
class_name Character
extends RefCounted
var char_name: String
var movement: Movement
func _init(new_name: String, new_movement: Movement) -> void:
char_name = new_name
movement = new_movement
func move() -> String:
return char_name + " " + movement.move()
# main.gd
var bat := Character.new("Bat", Walk.new())
print(bat.move()) # Bat läuft
bat.movement = Fly.new()
print(bat.move()) # Bat fliegtHier erweitern Walk und Fly eine kleine Movement-Klasse: Die beiden Techniken funktionieren zusammen. Eine GDScript-Klasse kann nur eine Klasse extend, daher erhält ein Objekt durch Komposition ebenfalls mehrere Fähigkeiten, indem es mehrere Bestandteile enthält.
Verwende Vererbung für eine echte „ist ein“-Beziehung, die sich niemals ändert. Verwende Komposition, wenn die Klasse etwas „hat“ oder wenn der Teil während des Spiels ausgetauscht werden muss.
Aufgabe
EinfachEin Ritter erbt nicht von einer Waffe: Ein Ritter hat eine Waffe und kann sie wechseln. Erstelle beide Klassen.
Weaponspeichertweapon_nameunddamage, die beide vom Konstruktor festgelegt werden:Weapon.new("Sword", 7).Knightspeichertknight_name(aus dem Konstruktor) und eine Waffe, die anfangsnullist.equip(new_weapon)ersetzt die Waffe unddrop()setzt sie wieder aufnull.attack()gibtAda attacks with Sword for 7 damagezurück, wobei der Name des Ritters sowie der Name und der Schaden der Waffe verwendet werden, oderAda has no weapon, wenn keine vorhanden ist.
Der bereitgestellte Code liest den Namen des Ritters und die Anzahl der folgenden Befehle (equip Sword 7, drop, attack) ein und führt sie aus. Für Ada und attack, equip Sword 7, attack, equip Axe 12, attack, drop, attack gibt er Folgendes aus:
Ada has no weapon
Equipped Sword
Ada attacks with Sword for 7 damage
Equipped Axe
Ada attacks with Axe for 12 damage
Dropped the weapon
Ada has no weaponDein Code gehört in weapon.gd und knight.gd. main.gd enthält den bereitgestellten Ein-/Ausgabecode und kann nicht bearbeitet werden.
Probier es selbst
extends Node
func _ready():
# Vorgegebener Ein-/Ausgabecode: unverändert lassen
var knight := Knight.new(OS.read_string_from_stdin())
var count := int(OS.read_string_from_stdin())
for i in count:
var parts := OS.read_string_from_stdin().split(" ")
if parts[0] == "equip":
knight.equip(Weapon.new(parts[1], int(parts[2])))
print("Equipped ", parts[1])
elif parts[0] == "drop":
knight.drop()
print("Dropped the weapon")
else:
print(knight.attack())
Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.
Alle Lektionen in Objektorientierte Programmierung
1Grundlagen der OOP
Externe DateienEinführung in die OOPKlassen vs. ObjekteDas Schlüsselwort selfMethodenEigenschaftenKonstruktor (_init)Wiederholung – Einfacher Taschenrechner4Polymorphie
Methodenüberschreibung erneut betrachtetDuck-TypisierungAbstrakte Klassen und MethodenSchnittstellendesignRückblick – Formrechner2Klassenattribute
Instanz- vs. statische VariablenStatische FunktionenProperty-SetterProperty-GetterPrivat nach KonventionWiederholung – Bankkontenverwaltung5Kapselung
Öffentliche und private MemberValidierung mit SetternInformationsverbergenSchreibgeschützte EigenschaftenRückblick – System zur Schülerverwaltung8Fortgeschrittene OOP-Konzepte
Komposition vs. VererbungInnere KlassenNodes als KomponentenRefCounted vs. NodeEnums in Klassen11Projekt: Bibliotheksverwaltung
ProjektübersichtBuch- und Benutzerklassen3Vererbung
Grundlegende VererbungDas Schlüsselwort superMethodenüberschreibungVererbungskettenTypprüfungen mit isWiederholung – Mitarbeiterhierarchie6Spezielle Methoden
Einführung in virtuelle MethodenDie _to_string-MethodeBenutzerdefinierte IteratorenRückblick – Benutzerdefinierte Liste9Entwurfsmuster Teil 1
Einführung in EntwurfsmusterSingleton-MusterFactory-MusterBeobachter-MusterStrategie-Muster12Abschließende Herausforderungen
E-Learning-PlattformBankensystemEntwicklung von SpielfigurenFahrzeugvermietungÜbe selbstständig: Online-GDScript-Compiler