Menu
CoddyTech

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) -> void

La 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.

challenge icon

Sfida

Medio

Crea 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 proprio size, conta come 1 file e list_lines() stampa il rientro, poi NAME (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 e list_lines() stampa il rientro, poi NAME/ (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: 2

Il 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()])
quiz iconMettiti alla prova

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

Esercitati da solo: Compilatore GDScript online