Menu
CoddyTech

Wzorzec stanu

Część sekcji Programowanie obiektowe ścieżki GDScript w Coddy. Lekcja 54 z 65.

Wzorzec State przypisuje obiektowi osobny obiekt stanu dla każdego trybu, w którym może się on znajdować. Obiekt przekazuje wykonywaną pracę aktualnemu stanowi, więc jego zachowanie zmienia się wraz ze zmianą stanu.

W przypadku kilku stanów wystarczą enum i instrukcja match. Gdy każdy stan reaguje na kilka działań na swój sposób, instrukcja match rozrasta się wraz z każdym stanem i każdym działaniem. Wzorzec State przenosi reguły każdego stanu do jego własnej klasy:

# hero_state.gd
@abstract
class_name HeroState
extends RefCounted

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

handle() zwraca następny stan: nowy obiekt stanu, na który należy przejść, albo self, aby pozostać w bieżącym stanie.

# 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 jest zapisany w ten sam sposób: jego nazwa to "Running", a "stop" zwraca IdleState.new(). Bohater, czyli kontekst, przechowuje bieżący stan i pyta go, co ma zrobić:

# 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

To samo działanie oznacza różne rzeczy w różnych stanach: "stop" nic nie robi, gdy bohater jest bezczynny, a zatrzymuje go, gdy biegnie. Każda reguła znajduje się w klasie danego stanu, więc nowy stan, taki jak skakanie, oznacza jedną nową klasę.

Stany zwracają następny stan zamiast przechowywać bohatera. Stan, który przechowywałby bohatera, gdyby bohater przechowywał stan, tworzyłby cykl referencji, a żaden z tych obiektów nigdy nie zostałby zwolniony.

challenge icon

Wyzwanie

Łatwy

Drzwi są Locked, Closed lub Open. DoorState (door_state.gd) deklaruje get_state_name() i handle(action), a LockedState (locked_state.gd) jest już napisany: unlock zmienia stan drzwi na Closed. Napisz:

  • ClosedState.handle(): open zwraca nowy OpenState, lock nowy LockedState, a w pozostałych przypadkach self.
  • OpenState.handle(): close zwraca nowy ClosedState, a w pozostałych przypadkach self.
  • Door.handle_action(action): prosi bieżący stan o następny stan. Jeśli jest to ten sam obiekt, wypisz ACTION: still STATE; w przeciwnym razie wypisz ACTION: OLD -> NEW i przejdź do nowego stanu.

Drzwi zaczynają w stanie Locked. Dostarczony kod odczytuje liczbę kolejnych akcji, a następnie po jednej akcji w każdym wierszu, i wypisuje stan końcowy. Dla open, unlock, open, lock wynik to:

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

Twój kod umieść w plikach door.gd, closed_state.gd i open_state.gd. main.gd zawiera dostarczony kod wejścia/wyjścia i nie można go edytować.

Spróbuj swoich sił

extends Node

func _ready():
	# Dostarczony kod wejścia/wyjścia: pozostaw go bez zmian
	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 iconSprawdź się

Ta lekcja zawiera krótki quiz. Zacznij lekcję, żeby na niego odpowiedzieć i śledzić swoje postępy.

Wszystkie lekcje w sekcji Programowanie obiektowe

Poćwicz samodzielnie: Kompilator GDScript online