Duck-Typisierung
Teil des Abschnitts Objektorientierte Programmierung der GDScript-Journey von Coddy. Lektion 22 von 65.
Duck-Typing interessiert sich dafür, was ein Objekt tun kann, nicht zu welcher Klasse es gehört. Wenn es die Methode hat, die du aufrufst, funktioniert der Aufruf. Der Name stammt von der Redewendung „Wenn es wie eine Ente schwimmt und wie eine Ente quakt, ist es eine Ente“.
Diese beiden Klassen haben kein gemeinsames übergeordnetes Element, aber beide verfügen über swim() und quack():
# duck.gd
class_name Duck
extends RefCounted
func swim() -> String:
return "Duck swimming"
func quack() -> String:
return "Quack!"# robot.gd
class_name Robot
extends RefCounted
func swim() -> String:
return "Robot swimming with propellers"
func quack() -> String:
return "Robot sound: BEEP BEEP!"Ein Parameter ohne Typ akzeptiert jedes Objekt. Die Funktion ruft die Methoden auf, und jedes Objekt führt seine eigenen aus:
func make_it_swim_and_quack(thing) -> void:
print(thing.swim())
print(thing.quack())make_it_swim_and_quack(Duck.new())
make_it_swim_and_quack(Robot.new())Duck swimming
Quack!
Robot swimming with propellers
Robot sound: BEEP BEEP!Für das Überschreiben ist eine gemeinsame Elternklasse erforderlich; Duck-Typing nicht. Bei der Typangabe des Parameters als thing: Duck wäre das Übergeben eines Robot ein Parse-Fehler.
Wenn einem Objekt die Methode fehlt, bricht der Aufruf die Funktion mit einem Fehler ab, z. B. Invalid call. Nonexistent function 'quack'. Frage zuerst mit has_method() nach; dabei wird der Methodenname als String übergeben:
var things := [Duck.new(), Rock.new()]
for thing in things:
if thing.has_method("quack"):
print(thing.quack())
else:
print("This one cannot quack")Quack!
This one cannot quackDer Preis: Mit Typen erkennt Godot einen falschen Methodennamen, bevor das Programm ausgeführt wird; bei Duck-Typing findest du es erst heraus, wenn die Zeile ausgeführt wird. Verwende es für Objekte nicht verwandter Klassen, die einige Methoden gemeinsam haben.
Aufgabe
EinfachEine Explosion beschädigt alles in der Nähe, das Schaden nehmen kann. Die Objekte haben keine gemeinsame Elternklasse. player.gd und rock.gd sind fertig: Ein Player hat take_damage(amount) und status(), ein Rock hat nur status().
- Gib in
crate.gdCrateeine Variablebroken, die mitfalsebeginnt, eine Methodetake_damage(amount), die die Kiste bei einem Betrag von10oder mehr zerbricht, und eine Methodestatus(), dieCrate: brokenoderCrate: intactzurückgibt. - Schreibe in
explosion.gddie statische Methodehit_all(things, amount). Sie ruft bei jedem Objekt im Array, das diese Methode hat,take_damage(amount)auf und gibt zurück, wie viele Objekte sie getroffen hat.
Der bereitgestellte Code liest eine Zeile mit Objektnamen (player, crate oder rock), dann die Anzahl der folgenden Explosionen und den Schaden jeder Explosion. Er gibt aus, wie viele Objekte jede Explosion getroffen hat, und danach den Status jedes Objekts. Für player crate rock und Explosionen mit 5 und 20 gibt er Folgendes aus:
Explosion 1 hit 2 objects
Explosion 2 hit 2 objects
Player: 75 HP
Crate: broken
Rock: solidDein Code gehört in crate.gd und explosion.gd. main.gd enthält den bereitgestellten Ein-/Ausgabecode und darf nicht bearbeitet werden.
Probier es selbst
extends Node
func _ready():
# Vorgegebener Eingabe-/Ausgabecode: Lass ihn unverändert
var things := []
for word in OS.read_string_from_stdin().split(" "):
if word == "player":
things.append(Player.new())
elif word == "crate":
things.append(Crate.new())
else:
things.append(Rock.new())
var count := int(OS.read_string_from_stdin())
for i in count:
var hits := Explosion.hit_all(things, int(OS.read_string_from_stdin()))
print("Explosion %d hit %d objects" % [i + 1, hits])
for thing in things:
print(thing.status())
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