Pattern Composite
Fa parte della sezione Programmazione orientata agli oggetti del percorso GDScript di Coddy. Lezione 55 di 65.
Il pattern Composite crea alberi in cui un singolo oggetto e un gruppo di oggetti hanno gli stessi metodi, così il codice può trattare un intero gruppo come un unico elemento. Una borsa contiene oggetti, una borsa può contenere altre borse e puoi chiedere a ciascuna il suo peso nello stesso modo.
Entrambi i tipi condividono un genitore astratto:
# entry.gd
@abstract
class_name Entry
extends RefCounted
var entry_name: String
@abstract func get_weight() -> int
@abstract func display(indent: String) -> voidLa foglia è un singolo elemento:
# 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, ")")Il composito contiene altre voci e risponde interrogandole una per una. Una borsa pesa 1 da sola:
# 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() non controlla mai se una voce è un oggetto o una borsa: chiama lo stesso metodo su ciascuna di esse, e una borsa al suo interno risponde chiedendo il peso dei propri contenuti. Questa ricorsione attraversa l'intero albero, per quanto sia profondo.
I collegamenti vanno in una sola direzione, da una cartella ai suoi contenuti. Se ogni elemento memorizzasse anche la cartella in cui si trova, ogni cartella e i suoi elementi punterebbero gli uni agli altri, creando un ciclo di riferimenti che non viene mai liberato. Quando un elemento ha bisogno di sapere dove si trova, passa questa informazione come argomento, proprio come display() passa il rientro.
Sfida
MedioCrea un albero di cartelle con il pattern Composite. DiskEntry (disk_entry.gd) dichiara get_size(), count_files() e list_lines(indent). Scrivi:
FileEntry: la sua dimensione è il propriosize, conta come1file elist_lines()stampa il rientro, poiNAME (SIZE KB).FolderEntry:add(entry)memorizza un elemento figlio. La sua dimensione è la somma delle dimensioni dei suoi elementi figli, il suo conteggio dei file è la somma dei loro conteggi elist_lines()stampa il rientro, poiNAME/ (SIZE KB), quindi elenca ogni elemento figlio con il rientro seguito da due spazi.
Il codice fornito crea l’albero a partire dall’input (il numero di righe successive, poi folder NAME, file NAME SIZE oppure end, che chiude la cartella corrente), lo elenca e stampa i totali. Per una cartella Root che contiene notes.txt (4 KB) e una cartella Music con song.mp3 (300 KB), l’output è:
Root/ (304 KB)
notes.txt (4 KB)
Music/ (300 KB)
song.mp3 (300 KB)
Total: 304 KB, files: 2Il tuo codice va in file_entry.gd e folder_entry.gd. main.gd contiene il codice fornito per l’input/output e non può essere modificato.
Provalo tu
extends Node
func _ready():
# Codice di input/output fornito: lascialo così com'è
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()])
Questa lezione include un breve quiz. Inizia la lezione per rispondere e tenere traccia dei tuoi progressi.
Tutte le lezioni di Programmazione orientata agli oggetti
1Fondamenti della programmazione orientata agli oggetti
File esterniIntroduzione alla programmazione orientata agli oggettiClassi e oggetti a confrontoLa parola chiave selfMetodiProprietàCostruttore (_init)Riepilogo - Calcolatrice semplice4Polimorfismo
Ripasso dell'override dei metodiDuck typingClassi e metodi astrattiProgettazione delle interfacceRiepilogo - Calcolatore di forme7Segnali
Che cos'è un segnale?Dichiarare ed emettereCollegarsi ai segnaliSegnali con argomentiRipasso - Sistema della salute10Pattern di progettazione, parte 2
Pattern CommandPattern AdapterPattern DecoratorPattern Template MethodPattern StatePattern Composite2Proprietà delle classi
Variabili di istanza e staticheFunzioni staticheSetter delle proprietàGetter delle proprietàPrivato per convenzioneRiepilogo - Gestore di conti bancari5Incapsulamento
Membri pubblici e privatiValidazione con i setterNascondimento delle informazioniProprietà di sola letturaRiepilogo - Sistema di gestione dei dati degli studenti8Concetti avanzati di OOP
Composizione vs ereditarietàClassi interneNodi come componentiRefCounted vs NodeEnum nelle classi11Progetto: Gestione della biblioteca
Panoramica del progettoClassi Libro e Utente3Ereditarietà
Ereditarietà di baseLa parola chiave superOverride dei metodiCatene di ereditarietàControlli di tipo con isRipasso - Gerarchia dei dipendenti6Metodi speciali
Introduzione ai metodi virtualiIl metodo _to_stringIteratori personalizzatiRiepilogo - Lista personalizzata9Modelli di progettazione, parte 1
Introduzione ai modelli di progettazionePattern SingletonPattern FactoryPattern ObserverPattern StrategyEsercitati da solo: Compilatore GDScript online