Menu
CoddyTech

컴포지트 패턴

Coddy GDScript 여정의 객체 지향 프로그래밍 섹션에 포함된 레슨. 65개 중 55번째.

Composite 패턴은 단일 객체와 객체 그룹이 동일한 메서드를 가지는 트리를 구성하므로, 코드는 전체 그룹을 하나의 항목처럼 다룰 수 있습니다. 가방에는 항목을 담을 수 있고, 가방 안에 다른 가방을 담을 수도 있으며, 어느 가방이든 같은 방식으로 무게를 물어볼 수 있습니다.

두 종류는 추상 부모를 공유합니다:

# entry.gd
@abstract
class_name Entry
extends RefCounted

var entry_name: String

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

잎은 단일 항목입니다:

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

복합 객체는 다른 항목들을 담고 있으며, 각 항목에 물어보는 방식으로 답을 구합니다. 가방 자체의 무게는 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()는 항목인지 가방인지 확인하지 않습니다. 각 항목에서 같은 메서드를 호출하고, 안에 있는 가방은 자신의 내용물에 물어보며 답합니다. 이 재귀는 트리의 깊이와 관계없이 트리 전체를 순회합니다.

링크는 가방에서 그 안의 항목으로 한 방향으로 이어집니다. 모든 항목이 자신이 들어 있는 가방도 저장한다면, 각 가방과 그 항목은 서로를 가리키게 되어 절대 해제되지 않는 참조 순환이 생깁니다. 항목이 자신이 어디에 있는지 알아야 할 때는 display()가 들여쓰기를 전달하는 것처럼 해당 정보를 인수로 전달하세요.

challenge icon

챌린지

중급

Composite 패턴을 사용해 폴더 트리를 만드세요. DiskEntry(disk_entry.gd)는 get_size(), count_files(), list_lines(indent)를 선언합니다. 다음을 작성하세요.

  • FileEntry: 크기는 자체 size이며, 파일 수는 1이고, list_lines()는 들여쓰기 다음에 NAME (SIZE KB)를 출력합니다.
  • FolderEntry: add(entry)는 자식 항목을 저장합니다. 크기는 자식 항목 크기의 합이고, 파일 수는 자식 항목 파일 수의 합입니다. list_lines()는 들여쓰기 다음에 NAME/ (SIZE KB)를 출력한 다음, 들여쓰기에 공백 두 개를 더해 모든 자식 항목을 나열합니다.

제공된 코드는 입력(이어지는 줄의 개수, 다음으로 folder NAME, file NAME SIZE 또는 현재 폴더를 닫는 end)을 바탕으로 트리를 만들고, 트리를 나열한 뒤 합계를 출력합니다. notes.txt(4 KB)와 song.mp3(300 KB)가 있는 Music 폴더를 포함하는 Root 폴더의 출력은 다음과 같습니다.

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

코드는 file_entry.gd와 folder_entry.gd에 작성하세요. main.gd에는 제공된 입출력 코드가 있으며 수정할 수 없습니다.

직접 해보기

extends Node

func _ready():
	# 제공된 입출력 코드: 그대로 유지하세요
	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 icon실력 점검

이 레슨에는 짧은 퀴즈가 포함되어 있습니다. 레슨을 시작해 문제를 풀고 진행 상황을 기록하세요.

객체 지향 프로그래밍의 모든 레슨

직접 연습해 보세요: 온라인 GDScript 컴파일러