Menu
CoddyTech

Pattern State

Fa parte della sezione Programmazione orientata agli oggetti del percorso GDScript di Coddy. Lezione 54 di 65.

Il pattern State assegna a un oggetto un oggetto di stato separato per ogni modalità in cui può trovarsi. L'oggetto delega il proprio lavoro allo stato corrente, così il suo comportamento cambia quando cambia lo stato.

Per pochi stati, un enum e un match sono sufficienti. Quando ogni stato reagisce a diverse azioni a modo suo, quel match cresce con ogni stato e ogni azione. Il pattern State sposta le regole di ogni stato nella propria classe:

# hero_state.gd
@abstract
class_name HeroState
extends RefCounted

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

handle() restituisce lo stato successivo: un nuovo oggetto stato a cui passare, oppure self per restare.

# 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 è scritto allo stesso modo: il suo nome è "Running" e "stop" restituisce IdleState.new(). L'eroe, il contesto, mantiene lo stato corrente e gli chiede cosa fare:

# 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 stessa azione ha significati diversi in stati diversi: "stop" non fa nulla quando l'eroe è inattivo e ferma un eroe in movimento. Ogni regola si trova nella classe del suo stato, quindi un nuovo stato, come il salto, corrisponde a una nuova classe.

Gli stati restituiscono lo stato successivo invece di memorizzare l’eroe. Uno stato che conservasse l’eroe mentre l’eroe conserva lo stato creerebbe un ciclo di riferimenti, e nessuno dei due oggetti verrebbe mai liberato.

challenge icon

Sfida

Facile

Una porta è Locked, Closed oppure Open. DoorState (door_state.gd) dichiara get_state_name() e handle(action), e LockedState (locked_state.gd) è già scritto: unlock porta la porta allo stato Closed. Scrivi:

  • ClosedState.handle(): open restituisce un nuovo OpenState, lock un nuovo LockedState, qualsiasi altra cosa self.
  • OpenState.handle(): close restituisce un nuovo ClosedState, qualsiasi altra cosa self.
  • Door.handle_action(action): chiede allo stato corrente quale sarà il prossimo. Se è lo stesso oggetto, stampa ACTION: still STATE; altrimenti stampa ACTION: OLD -> NEW e passa al nuovo stato.

La porta inizia nello stato Locked. Il codice fornito legge quante azioni seguono, poi un'azione per riga, e stampa lo stato finale. Per open, unlock, open, lock l'output è:

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

Il tuo codice va in door.gd, closed_state.gd e open_state.gd. main.gd contiene il codice fornito per l'input/output e non può essere modificato.

Provalo tu

extends Node

func _ready():
	# Codice di input/output fornito: lascialo così com'è
	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 iconMettiti alla prova

Questa lezione include un breve quiz. Inizia la lezione per rispondere e tenere traccia dei tuoi progressi.

Tutte le lezioni di Programmazione orientata agli oggetti

Esercitati da solo: Compilatore GDScript online