Kompositum-Muster
Teil des Abschnitts Objektorientierte Programmierung der GDScript-Journey von Coddy. Lektion 55 von 65.
Das Composite-Muster erstellt Bäume, in denen ein einzelnes Objekt und eine Gruppe von Objekten dieselben Methoden haben, sodass Code eine ganze Gruppe wie ein einzelnes Element behandeln kann. Eine Tasche enthält Elemente, eine Tasche kann andere Taschen enthalten, und du kannst jede von ihnen auf dieselbe Weise nach ihrem Gewicht fragen.
Beide Arten haben eine abstrakte Basisklasse:
# entry.gd
@abstract
class_name Entry
extends RefCounted
var entry_name: String
@abstract func get_weight() -> int
@abstract func display(indent: String) -> voidDas Blatt ist ein einzelnes Element:
# item.gd
class_name Item
extends Entry
var weight: int
func _init(n: String, w: int) -> void:
entry_name = n
weight = w
func get_weight() -> int:
return weight
func display(indent: String) -> void:
print(indent, entry_name, " (", weight, ")")Das zusammengesetzte Objekt enthält weitere Einträge und fragt jeden von ihnen ab. Eine Tasche wiegt selbst 1:
# bag.gd
class_name Bag
extends Entry
var contents: Array[Entry] = []
func _init(n: String) -> void:
entry_name = n
func add(entry: Entry) -> void:
contents.append(entry)
func get_weight() -> int:
var total := 1
for entry in contents:
total += entry.get_weight()
return total
func display(indent: String) -> void:
print(indent, entry_name, " [", get_weight(), "]")
for entry in contents:
entry.display(indent + " ")# main.gd
var pouch := Bag.new("Pouch")
pouch.add(Item.new("Gem", 2))
var backpack := Bag.new("Backpack")
backpack.add(Item.new("Sword", 8))
backpack.add(pouch)
backpack.display("")Backpack [12]
Sword (8)
Pouch [3]
Gem (2)Bag.get_weight() prüft nie, ob ein Eintrag ein Gegenstand oder eine Tasche ist: Es ruft bei jedem dieselbe Methode auf, und eine Tasche darin antwortet, indem sie ihren eigenen Inhalt abfragt. Diese Rekursion durchläuft den gesamten Baum, egal wie tief er ist.
Verknüpfungen verlaufen in eine Richtung, von einer Tasche zu ihrem Inhalt. Wenn jeder Eintrag auch die Tasche speichern würde, in der er sich befindet, würden jede Tasche und ihre Einträge aufeinander verweisen – ein Referenzzyklus, der nie freigegeben wird. Wenn ein Eintrag wissen muss, wo er sich befindet, übergib diese Information als Argument, so wie display() die Einrückung übergibt.
Aufgabe
MittelErstelle mit dem Composite-Muster einen Ordnerbaum. DiskEntry (disk_entry.gd) deklariert get_size(), count_files() und list_lines(indent). Schreibe:
FileEntry: Seine Größe ist sein eigenessize, es zählt als1Datei, undlist_lines()gibt die Einrückung und dannNAME (SIZE KB)aus.FolderEntry:add(entry)speichert ein untergeordnetes Element. Seine Größe ist die Summe der Größen seiner untergeordneten Elemente, seine Dateianzahl die Summe ihrer Dateianzahlen, undlist_lines()gibt die Einrückung und dannNAME/ (SIZE KB)aus und listet anschließend jedes untergeordnete Element mit der Einrückung plus zwei Leerzeichen auf.
Der bereitgestellte Code erstellt den Baum aus der Eingabe (wie viele Zeilen folgen, dann folder NAME, file NAME SIZE oder end, wodurch der aktuelle Ordner geschlossen wird), listet ihn auf und gibt die Gesamtwerte aus. Für einen Root-Ordner mit notes.txt (4 KB) und einem Music-Ordner mit song.mp3 (300 KB) lautet die Ausgabe:
Root/ (304 KB)
notes.txt (4 KB)
Music/ (300 KB)
song.mp3 (300 KB)
Total: 304 KB, files: 2Dein Code gehört in file_entry.gd und folder_entry.gd. main.gd enthält den bereitgestellten Ein-/Ausgabecode und kann nicht bearbeitet werden.
Probier es selbst
extends Node
func _ready():
# Vorgegebener Eingabe-/Ausgabecode: unverändert lassen
var open_folders: Array[FolderEntry] = []
var root: FolderEntry = null
var count := int(OS.read_string_from_stdin())
for i in count:
var parts := OS.read_string_from_stdin().split(" ")
match parts[0]:
"folder":
var folder := FolderEntry.new(parts[1])
if open_folders.is_empty():
root = folder
else:
open_folders.back().add(folder)
open_folders.append(folder)
"file":
open_folders.back().add(FileEntry.new(parts[1], int(parts[2])))
"end":
open_folders.pop_back()
root.list_lines("")
print("Total: %d KB, files: %d" % [root.get_size(), root.count_files()])
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 – Formrechner7Signale
Was ist ein Signal?Deklarieren und AuslösenMit Signalen verbindenSignale mit ArgumentenRückblick – Gesundheitssystem10Entwurfsmuster Teil 2
Befehl-MusterAdapter-MusterDekorierer-MusterVorlagenmethoden-MusterZustandsmusterKompositum-Muster2Klassenattribute
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