Menu
CoddyTech

Patron État

Fait partie de la section Programmation orientée objet du Journey GDScript de Coddy. Leçon 54 sur 65.

Le patron État attribue à un objet un objet d’état distinct pour chaque mode dans lequel il peut se trouver. L’objet délègue son travail à l’état actuel, de sorte que son comportement change lorsque l’état change.

Pour quelques états, une énumération et un match suffisent. Lorsque chaque état réagit à plusieurs actions à sa manière, ce match s’allonge à chaque état et à chaque action. Le patron État déplace les règles de chaque état dans sa propre classe :

# hero_state.gd
@abstract
class_name HeroState
extends RefCounted

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

handle() renvoie l’état suivant : un nouvel objet d’état vers lequel passer, ou self pour rester dans l’état actuel.

# 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 s’écrit de la même manière : son nom est "Running", et "stop" renvoie IdleState.new(). Le héros, le contexte, conserve l’état actuel et lui demande quoi faire :

# 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 même action signifie des choses différentes selon l’état : "stop" ne fait rien à l’arrêt et arrête un héros en mouvement. Chaque règle se trouve dans la classe de son état, donc un nouvel état, comme le saut, correspond à une nouvelle classe.

Les états renvoient l’état suivant au lieu de stocker le héros. Un état qui conserverait le héros tandis que le héros conserverait l’état créerait un cycle de références, et aucun des deux objets ne serait jamais libéré.

challenge icon

Défi

Facile

Une porte est Locked, Closed ou Open. DoorState (door_state.gd) déclare get_state_name() et handle(action), et LockedState (locked_state.gd) est écrit : unlock fait passer la porte à l’état Closed. Écris :

  • ClosedState.handle() : open renvoie un nouvel OpenState, lock un nouvel LockedState, et toute autre valeur self.
  • OpenState.handle() : close renvoie un nouveau ClosedState, et toute autre valeur self.
  • Door.handle_action(action) : demande à l’état actuel quel est le suivant. S’il s’agit du même objet, affiche ACTION: still STATE ; sinon, affiche ACTION: OLD -> NEW et passe au nouvel état.

La porte commence à l’état Locked. Le code fourni lit le nombre d’actions à effectuer, puis une action par ligne, et affiche l’état final. Pour open, unlock, open, lock, la sortie est :

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

Ton code va dans door.gd, closed_state.gd et open_state.gd. main.gd contient le code fourni pour les entrées et sorties et ne peut pas être modifié.

Essayez vous-même

extends Node

func _ready():
	# Code d'entrée/sortie fourni : conservez-le tel quel
	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 iconTestez-vous

Cette leçon comprend un petit quiz. Commencez la leçon pour y répondre et suivre votre progression.

Toutes les leçons de Programmation orientée objet

Entraînez-vous par vous-même : Compilateur GDScript en ligne