Menu
CoddyTech

Wzorzec kompozytu

Część sekcji Programowanie obiektowe ścieżki GDScript w Coddy. Lekcja 55 z 65.

Wzorzec Composite tworzy drzewa, w których pojedynczy obiekt i grupa obiektów mają te same metody, dzięki czemu kod może traktować całą grupę jak jeden element. Torba zawiera przedmioty, może zawierać inne torby, a o wagę każdej z nich pytasz w ten sam sposób.

Oba rodzaje mają wspólną klasę abstrakcyjną:

# entry.gd
@abstract
class_name Entry
extends RefCounted

var entry_name: String

@abstract func get_weight() -> int
@abstract func display(indent: String) -> void

Liść jest pojedynczym elementem:

# 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, ")")

Kontener złożony przechowuje inne elementy i odpowiada, pytając każdy z nich. Sam worek waży 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() nigdy nie sprawdza, czy wpis jest przedmiotem, czy torbą: wywołuje tę samą metodę dla każdego z nich, a znajdująca się w środku torba odpowiada, pytając o własną zawartość. Ta rekurencja przechodzi przez całe drzewo, niezależnie od jego głębokości.

Łącza prowadzą tylko w jedną stronę — od torby do jej zawartości. Gdyby każdy wpis przechowywał również informację o torbie, w której się znajduje, każda torba i jej wpisy wskazywałyby na siebie nawzajem, tworząc cykl odwołań, który nigdy nie zostanie zwolniony. Gdy wpis musi wiedzieć, gdzie się znajduje, przekaż tę informację jako argument, tak jak display() przekazuje wcięcie.

challenge icon

Wyzwanie

Średni

Zbuduj drzewo folderów z użyciem wzorca Composite. DiskEntry (disk_entry.gd) deklaruje get_size(), count_files() i list_lines(indent). Napisz:

  • FileEntry: jego rozmiar to jego własne size, jest liczony jako 1 plik, a list_lines() wypisuje wcięcie, a następnie NAME (SIZE KB).
  • FolderEntry: add(entry) przechowuje element podrzędny. Jego rozmiar to suma rozmiarów elementów podrzędnych, liczba plików to suma ich liczby plików, a list_lines() wypisuje wcięcie, a następnie NAME/ (SIZE KB), po czym wyświetla każdy element podrzędny z wcięciem powiększonym o dwie spacje.

Dostarczony kod buduje drzewo na podstawie danych wejściowych (ile wierszy następuje dalej, a potem folder NAME, file NAME SIZE lub end, które zamyka bieżący folder), wyświetla je i wypisuje sumy. Dla folderu Root zawierającego notes.txt (4 KB) oraz folderu Music z plikiem song.mp3 (300 KB) wynik to:

Root/ (304 KB)
  notes.txt (4 KB)
  Music/ (300 KB)
    song.mp3 (300 KB)
Total: 304 KB, files: 2

Umieść swój kod w plikach file_entry.gd i folder_entry.gd. main.gd zawiera dostarczony kod wejścia/wyjścia i nie można go edytować.

Spróbuj swoich sił

extends Node

func _ready():
	# Dostarczony kod wejścia/wyjścia: pozostaw go bez zmian
	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 iconSprawdź się

Ta lekcja zawiera krótki quiz. Zacznij lekcję, żeby na niego odpowiedzieć i śledzić swoje postępy.

Wszystkie lekcje w sekcji Programowanie obiektowe

Poćwicz samodzielnie: Kompilator GDScript online