Menu
CoddyTech

Padrão State

Parte da seção Programação orientada a objetos do Journey de GDScript da Coddy. Lição 54 de 65.

O padrão State dá a um objeto um objeto de estado separado para cada modo em que pode estar. O objeto delega seu trabalho ao estado atual, então seu comportamento muda quando o estado muda.

Para poucos estados, um enum e um match são suficientes. Quando cada estado reage a várias ações à sua maneira, esse match cresce a cada estado e a cada ação. O padrão State move as regras de cada estado para sua própria classe:

# hero_state.gd
@abstract
class_name HeroState
extends RefCounted

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

handle() retorna o próximo estado: um novo objeto de estado para o qual mudar, ou self para permanecer.

# 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 é escrito da mesma forma: seu nome é "Running", e "stop" retorna IdleState.new(). O herói, o contexto, mantém o estado atual e pergunta a ele o que fazer:

# 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

A mesma ação significa coisas diferentes em estados diferentes: "stop" não faz nada enquanto está ocioso e para um herói em movimento. Cada regra fica na classe de seu estado, então um novo estado, como pular, é uma nova classe.

Os estados retornam o próximo estado em vez de armazenar o herói. Um estado que mantivesse o herói enquanto o herói mantém o estado criaria um ciclo de referências, e nenhum dos objetos seria liberado.

challenge icon

Desafio

Fácil

Uma porta está Locked, Closed ou Open. DoorState (door_state.gd) declara get_state_name() e handle(action), e LockedState (locked_state.gd) já está implementado: unlock deixa a porta Closed. Escreva:

  • ClosedState.handle(): open retorna um novo OpenState, lock retorna um novo LockedState, e qualquer outra coisa retorna self.
  • OpenState.handle(): close retorna um novo ClosedState, e qualquer outra coisa retorna self.
  • Door.handle_action(action): solicita ao estado atual o próximo estado. Se for o mesmo objeto, imprime ACTION: still STATE; caso contrário, imprime ACTION: OLD -> NEW e muda para o novo estado.

A porta começa Locked. O código fornecido lê quantas ações vêm a seguir, depois uma ação por linha, e imprime o estado final. Para open, unlock, open, lock, a saída é:

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

Seu código deve ficar em door.gd, closed_state.gd e open_state.gd. main.gd contém o código fornecido de entrada/saída e não pode ser editado.

Experimente você mesmo

extends Node

func _ready():
	# Código de entrada/saída fornecido: mantenha-o 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 iconTeste seus conhecimentos

Esta lição inclui um quiz rápido. Comece a lição para respondê-lo e acompanhar seu progresso.

Todas as lições de Programação orientada a objetos

Pratique por conta própria: Compilador de GDScript online