Menu
CoddyTech

Patrón State

Parte de la sección Programación orientada a objetos del Journey de GDScript de Coddy. Lección 54 de 65.

El patrón State proporciona a un objeto un objeto de estado independiente para cada modo en el que puede estar. El objeto delega su trabajo en el estado actual, por lo que su comportamiento cambia cuando cambia el estado.

Para unos pocos estados, un enum y un match son suficientes. Cuando cada estado reacciona a varias acciones a su manera, ese match crece con cada estado y cada acción. El patrón State traslada las reglas de cada estado a su propia clase:

# hero_state.gd
@abstract
class_name HeroState
extends RefCounted

@abstract func get_state_name() -> String
@abstract func handle(action: String) -> HeroState

handle() devuelve el siguiente estado: un nuevo objeto de estado al que cambiar, o self para quedarse.

# idle_state.gd
class_name IdleState
extends HeroState

func get_state_name() -> String:
	return "Idle"

func handle(action: String) -> HeroState:
	if action == "run":
		return RunState.new()
	return self

RunState se escribe de la misma manera: su nombre es "Running", y "stop" devuelve IdleState.new(). El héroe, el contexto, conserva el estado actual y le pregunta qué hacer:

# hero.gd
class_name Hero
extends RefCounted

var state: HeroState = IdleState.new()

func handle_action(action: String) -> void:
	var next := state.handle(action)
	if next != state:
		print(state.get_state_name(), " -> ", next.get_state_name())
		state = next
# main.gd
var hero := Hero.new()
for action in ["stop", "run", "run", "stop"]:
	hero.handle_action(action)
Idle -> Running
Running -> Idle

La misma acción significa cosas diferentes en distintos estados: "stop" no hace nada mientras está inactivo y detiene a un héroe que está corriendo. Cada regla reside en la clase de su estado, así que un estado nuevo, como saltar, es una clase nueva.

Los estados devuelven el siguiente estado en lugar de almacenar al héroe. Un estado que mantuviera al héroe mientras el héroe mantiene el estado formaría un ciclo de referencias, y ninguno de los dos objetos se liberaría nunca.

challenge icon

Desafío

Fácil

Una puerta está Locked, Closed o Open. DoorState (door_state.gd) declara get_state_name() y handle(action), y LockedState (locked_state.gd) ya está escrito: unlock hace que la puerta quede Closed. Escribe:

  • ClosedState.handle(): open devuelve un nuevo OpenState, lock uno nuevo LockedState, y cualquier otra cosa, self.
  • OpenState.handle(): close devuelve un nuevo ClosedState, y cualquier otra cosa, self.
  • Door.handle_action(action): pregunta al estado actual cuál es el siguiente. Si es el mismo objeto, imprime ACTION: still STATE; de lo contrario, imprime ACTION: OLD -> NEW y cambia al nuevo estado.

La puerta empieza en Locked. El código proporcionado lee cuántas acciones siguen, luego una acción por línea, e imprime el estado final. Para open, unlock, open, lock el resultado es:

open: still Locked
unlock: Locked -> Closed
open: Closed -> Open
lock: still Open
Final state: Open

Tu código va en door.gd, closed_state.gd y open_state.gd. main.gd contiene el código proporcionado para la entrada y salida, y no se puede editar.

Pruébalo tú mismo

extends Node

func _ready():
	# Código de entrada/salida proporcionado: mantenlo tal como está
	var door := Door.new()
	var count := int(OS.read_string_from_stdin())
	for i in count:
		door.handle_action(OS.read_string_from_stdin())
	print("Final state: ", door.state.get_state_name())
quiz iconPonte a prueba

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

Practica por tu cuenta: Compilador de GDScript online