Padrão Composite
Parte da seção Programação orientada a objetos do Journey de GDScript da Coddy. Lição 55 de 65.
O padrão Composite cria árvores nas quais um único objeto e um grupo de objetos têm os mesmos métodos, para que o código possa tratar um grupo inteiro como um único item. Uma sacola contém itens, uma sacola pode conter outras sacolas, e você pode perguntar o peso de qualquer uma delas da mesma maneira.
Ambos os tipos compartilham um pai abstrato:
# entry.gd
@abstract
class_name Entry
extends RefCounted
var entry_name: String
@abstract func get_weight() -> int
@abstract func display(indent: String) -> voidA folha é um único item:
# 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, ")")O composto contém outras entradas e responde consultando cada uma delas. Uma bolsa pesa 1 por si só:
# 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() nunca verifica se uma entrada é um item ou uma bolsa: ele chama o mesmo método em cada uma, e uma bolsa dentro de outra responde perguntando aos próprios conteúdos. Essa recursão percorre toda a árvore, por mais profunda que seja.
Os links seguem em uma só direção, de uma pasta para seu conteúdo. Se cada entrada também armazenasse a pasta em que está, cada pasta e suas entradas apontariam umas para as outras, formando um ciclo de referências que nunca é liberado. Quando uma entrada precisa saber onde está, passe essa informação como argumento, da mesma forma que display() passa a indentação.
Desafio
MédioCrie uma árvore de pastas usando o padrão Composite. DiskEntry (disk_entry.gd) declara get_size(), count_files() e list_lines(indent). Escreva:
FileEntry: seu tamanho é o própriosize, conta como1arquivo, elist_lines()imprime a indentação, depoisNAME (SIZE KB).FolderEntry:add(entry)armazena um filho. Seu tamanho é a soma dos tamanhos dos filhos, sua contagem de arquivos é a soma das contagens deles, elist_lines()imprime a indentação, depoisNAME/ (SIZE KB), e então lista cada filho com a indentação mais dois espaços.
O código fornecido cria a árvore a partir da entrada (quantas linhas vêm a seguir, depois folder NAME, file NAME SIZE ou end, que fecha a pasta atual), lista-a e imprime os totais. Para uma pasta Root contendo notes.txt (4 KB) e uma pasta Music com song.mp3 (300 KB), a saída é:
Root/ (304 KB)
notes.txt (4 KB)
Music/ (300 KB)
song.mp3 (300 KB)
Total: 304 KB, files: 2Seu código deve ficar em file_entry.gd e folder_entry.gd. main.gd contém o código fornecido de entrada/saída e não pode ser editado.
Experimente você mesmo
extends Node
func _ready():
# Código de entrada/saída fornecido: mantenha-o como está
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()])
Esta lição inclui um quiz rápido. Comece a lição para respondê-lo e acompanhar seu progresso.
Todas as lições de Programação orientada a objetos
1Fundamentos da POO
Arquivos externosIntrodução à POOClasses vs. objetosA palavra-chave selfMétodosPropriedadesConstrutor (_init)Revisão - Calculadora simples4Polimorfismo
Revisitando a Sobrescrita de MétodosDuck TypingClasses e Métodos AbstratosDesign de InterfacesRevisão - Calculadora de Formas7Sinais
O que é um sinal?Declarando e emitindoConectando-se a sinaisSinais com argumentosRevisão - Sistema de saúde10Padrões de Projeto Parte 2
Padrão CommandPadrão AdapterPadrão DecoratorPadrão Template MethodPadrão StatePadrão Composite2Propriedades de Classe
Variáveis de Instância vs. EstáticasFunções EstáticasMétodos Setters de PropriedadeMétodos Getters de PropriedadePrivado por ConvençãoRecapitulando: Gerenciador de Conta Bancária5Encapsulamento
Membros públicos e privadosValidação com settersOcultação de informaçõesPropriedades somente para leituraRevisão - Sistema de registros de alunos8Conceitos Avançados de POO
Composição vs. HerançaClasses InternasNós como ComponentesRefCounted vs. NodeEnums em Classes3Herança
Herança básicaA palavra-chave superSobrescrita de métodosCadeias de herançaVerificações de tipo com isRevisão - Hierarquia de funcionários6Métodos especiais
Introdução aos métodos virtuaisO método _to_stringIteradores personalizadosRevisão - Lista personalizada9Padrões de Projeto Parte 1
Introdução aos Padrões de ProjetoPadrão SingletonPadrão FactoryPadrão ObserverPadrão StrategyPratique por conta própria: Compilador de GDScript online