Patrón Command
Parte de la sección Programación orientada a objetos del Journey de GDScript de Coddy. Lección 50 de 65.
El patrón Command convierte una acción en un objeto. El objeto almacena todo lo necesario para ejecutar la acción, así que puedes guardarlo en una lista, ejecutarlo más tarde o deshacerlo.
Cada comando tiene los mismos dos métodos, declarados por una clase padre abstracta:
# command.gd
@abstract
class_name Command
extends RefCounted
@abstract func execute() -> void
@abstract func undo() -> voidUn comando concreto conserva el objeto sobre el que actúa, llamado receptor, y los datos de la acción. Aquí el receptor es un Hero con un 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 -= dxEl invocador ejecuta los comandos y los recuerda. Nunca necesita saber qué hace un comando:
# 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
3El historial es una pila: pop_back() toma el comando más reciente, así que deshacer recorre las acciones en orden inverso. Los mismos objetos te permiten reasignar teclas (una tecla se asigna a un comando), reproducir acciones (volver a ejecutar la lista guardada) y gestionar colas de turnos.
Las referencias van en un solo sentido: los comandos apuntan al héroe, y el héroe no sabe nada de ellos. Si el héroe también almacenara sus comandos, los objetos se mantendrían vivos mutuamente y nunca se liberarían.
Desafío
FácilUn héroe se mueve por una cuadrícula y cada movimiento se puede deshacer. Command (command.gd) declara execute() y undo(), y Hero (hero.gd) tiene x, y y position_text(). Escribe:
MoveCommand: el constructor guarda el héroe y los pasosstep_xystep_y.execute()suma los pasos axeydel héroe, yundo()los resta.CommandHistory:run(command)ejecuta el comando y lo guarda.undo_last()deshace el comando guardado más reciente, lo elimina y devuelvetrue; si no hay nada guardado, devuelvefalse.
El código proporcionado lee cuántas acciones siguen y luego una acción por línea (up, down, left, right o undo), e imprime la posición después de cada una. up disminuye y en uno. Para right, up, undo, undo, undo, la salida es:
right -> (1, 0)
up -> (1, -1)
undo -> (1, 0)
undo -> (0, 0)
Nothing to undoTu código va en move_command.gd y command_history.gd. main.gd contiene el código de entrada/salida proporcionado y no se puede editar.
Pruébalo tú mismo
extends Node
func _ready():
# Código de entrada/salida proporcionado: mantenlo tal 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 lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.
Todas las lecciones de Programación orientada a objetos
1Fundamentos de la programación orientada a objetos
Archivos externosIntroducción a la programación orientada a objetosClases vs. objetosLa palabra clave selfMétodosPropiedadesConstructor (_init)Repaso: calculadora sencilla4Polimorfismo
Repaso de la sobrescritura de métodosTipado patoClases y métodos abstractosDiseño de interfacesRepaso: calculadora de formas7Señales
¿Qué es una señal?Declarar y emitirConectarse a las señalesSeñales con argumentosRepaso: sistema de salud10Patrones de diseño, parte 2
Patrón CommandPatrón AdapterPatrón DecoratorPatrón Template MethodPatrón StatePatrón Composite2Propiedades de clase
Variables de instancia y estáticasFunciones estáticasMétodos setter de propiedadesMétodos getter de propiedadesPrivado por convenciónRepaso: administrador de cuentas bancarias5Encapsulación
Miembros públicos y privadosValidación con settersOcultación de informaciónPropiedades de solo lecturaRepaso: sistema de registros de estudiantes8Conceptos avanzados de POO
Composición frente a herenciaClases internasNodos como componentesRefCounted frente a NodeEnums en clases3Herencia
Herencia básicaLa palabra clave superSobrescritura de métodosCadenas de herenciaComprobaciones de tipo con isRepaso: jerarquía de empleados6Métodos especiales
Introducción a los métodos virtualesEl método _to_stringIteradores personalizadosRepaso: lista personalizada9Patrones de diseño, parte 1
Introducción a los patrones de diseñoPatrón SingletonPatrón FactoryPatrón ObserverPatrón StrategyPractica por tu cuenta: Compilador de GDScript online