Padrão Command
Parte da seção Programação orientada a objetos do Journey de GDScript da Coddy. Lição 50 de 65.
O padrão Command transforma uma ação em um objeto. O objeto armazena tudo o que é necessário para executar a ação, para que você possa mantê-lo em uma lista, executá-lo mais tarde ou desfazê-lo.
Todo comando tem os mesmos dois métodos, declarados por uma classe pai abstrata:
# command.gd
@abstract
class_name Command
extends RefCounted
@abstract func execute() -> void
@abstract func undo() -> voidUm comando concreto mantém o objeto sobre o qual atua, chamado receptor, e os dados da ação. Aqui, o receptor é um Hero com um var x := 0:
# move_command.gd
class_name MoveCommand
extends Command
var hero: Hero
var dx: int
func _init(target: Hero, step: int) -> void:
hero = target
dx = step
func execute() -> void:
hero.x += dx
func undo() -> void:
hero.x -= dxO invocador executa comandos e se lembra deles. Ele não precisa saber o que um comando faz:
# command_history.gd
class_name CommandHistory
extends RefCounted
var done: Array[Command] = []
func run(command: Command) -> void:
command.execute()
done.append(command)
func undo_last() -> void:
if done.is_empty():
return
var last: Command = done.pop_back()
last.undo()# main.gd
var hero := Hero.new()
var history := CommandHistory.new()
history.run(MoveCommand.new(hero, 3))
history.run(MoveCommand.new(hero, 2))
print(hero.x)
history.undo_last()
print(hero.x)5
3O histórico é uma pilha: pop_back() pega o comando mais recente, então desfazer percorre as ações na ordem inversa. Os mesmos objetos permitem remapear teclas (uma tecla corresponde a um comando), reproduzir ações (executar novamente a lista salva) e criar filas de turnos.
As referências vão em uma só direção: os comandos apontam para o herói, e o herói não sabe nada sobre eles. Se o herói também armazenasse seus comandos, os objetos manteriam uns aos outros vivos e nunca seriam liberados.
Desafio
FácilUm herói se move em uma grade, e cada movimento pode ser desfeito. Command (command.gd) declara execute() e undo(), e Hero (hero.gd) tem x, y e position_text(). Escreva:
MoveCommand: o construtor armazena o herói e os passosstep_xestep_y.execute()soma os passos axeydo herói, eundo()os subtrai.CommandHistory:run(command)executa o comando e o armazena.undo_last()desfaz o comando armazenado mais recente, remove-o e retornatrue; se não houver nada armazenado, retornafalse.
O código fornecido lê quantas ações seguem e, em seguida, uma ação por linha (up, down, left, right ou undo) e imprime a posição após cada uma. up diminui y em um. Para right, up, undo, undo, undo, a saída é:
right -> (1, 0)
up -> (1, -1)
undo -> (1, 0)
undo -> (0, 0)
Nothing to undoSeu código deve ficar em move_command.gd e command_history.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 hero := Hero.new()
var history := CommandHistory.new()
var steps := {"up": [0, -1], "down": [0, 1], "left": [-1, 0], "right": [1, 0]}
var count := int(OS.read_string_from_stdin())
for i in count:
var action := OS.read_string_from_stdin()
if action == "undo":
if history.undo_last():
print("undo -> ", hero.position_text())
else:
print("Nothing to undo")
else:
var step: Array = steps[action]
history.run(MoveCommand.new(hero, step[0], step[1]))
print(action, " -> ", hero.position_text())
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