Menu
CoddyTech

Padrão Decorator

Parte da seção Programação orientada a objetos do Journey de GDScript da Coddy. Lição 52 de 65.

O padrão Decorator adiciona comportamento a um objeto envolvendo-o em outro objeto com os mesmos métodos. Cada wrapper chama o objeto dentro dele e adiciona sua própria parte, e os wrappers podem ser encadeados em qualquer ordem.

# weapon.gd
class_name Weapon
extends RefCounted

func get_damage() -> int:
	return 10

func describe() -> String:
	return "Sword"

Um decorador estende Weapon, então pode ser usado onde se espera uma arma, e mantém a arma que envolve:

# fire_enchant.gd
class_name FireEnchant
extends Weapon

var inner: Weapon

func _init(wrapped: Weapon) -> void:
	inner = wrapped

func get_damage() -> int:
	return inner.get_damage() + 5

func describe() -> String:
	return inner.describe() + " + Fire"

SharpEnchant é escrito da mesma forma, mas seu get_damage() retorna inner.get_damage() * 2 e seu describe() adiciona " + Sharp".

# main.gd
var weapon: Weapon = Weapon.new()
print(weapon.describe(), ": ", weapon.get_damage())
weapon = FireEnchant.new(weapon)
weapon = SharpEnchant.new(weapon)
print(weapon.describe(), ": ", weapon.get_damage())
Sword: 10
Sword + Fire + Sharp: 30

As chamadas seguem para dentro: SharpEnchant pergunta a FireEnchant, que pergunta à espada. A espada retorna 10, o fogo transforma esse valor em 15, e o afiado dobra esse valor para 30. Na outra ordem, FireEnchant.new(SharpEnchant.new(Weapon.new())), o dano é 10 * 2 + 5, ou seja, 25.

Sem decoradores, cada combinação precisaria de sua própria subclasse: FireSword, SharpSword, FireSharpSword e assim por diante. Com eles, duas classes pequenas abrangem todas as combinações, e um novo encantamento exige apenas mais um arquivo.

challenge icon

Desafio

Médio

Uma cafeteria prepara bebidas com decoradores. Beverage (beverage.gd) declara cost() e description(); Espresso (espresso.gd, $3) e Tea (tea.gd, $2) já estão escritos. Cada decorador armazena em inner a bebida que envolve (os construtores já estão escritos). Sobrescreva cost() e description() em:

  • Milk: custa 1 a mais e adiciona + Milk à descrição.
  • Mocha: custa 2 a mais e adiciona + Mocha.
  • Large: dobra o custo de tudo o que está dentro dele e coloca Large no início da descrição.

O código fornecido lê quantos pedidos virão e, em seguida, um pedido por linha: a bebida (espresso ou tea) e seus adicionais, aplicados da esquerda para a direita. Para espresso milk large e tea mocha milk, a saída é:

Large Espresso + Milk: $8
Tea + Mocha + Milk: $5

Seu código deve ficar em milk.gd, mocha.gd e large.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 count := int(OS.read_string_from_stdin())
	for i in count:
		var words := OS.read_string_from_stdin().split(" ")
		var drink: Beverage = Tea.new()
		if words[0] == "espresso":
			drink = Espresso.new()
		for extra in words.slice(1):
			match extra:
				"milk":
					drink = Milk.new(drink)
				"mocha":
					drink = Mocha.new(drink)
				"large":
					drink = Large.new(drink)
		print("%s: $%d" % [drink.description(), drink.cost()])
quiz iconTeste seus conhecimentos

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

Pratique por conta própria: Compilador de GDScript online