State Pattern
Part of the Object Oriented Programming section of Coddy's GDScript journey. Lesson 54 of 65.
The State pattern gives an object a separate state object for each mode it can be in. The object hands its work to the current state, so its behavior changes when the state changes.
For a few states, an enum and a match are enough. When every state reacts to several actions in its own way, that match grows with every state and every action. The State pattern moves each state's rules into its own class:
# hero_state.gd
@abstract
class_name HeroState
extends RefCounted
@abstract func get_state_name() -> String
@abstract func handle(action: String) -> HeroStatehandle() returns the next state: a new state object to switch to, or self to stay.
# 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 selfRunState is written the same way: its name is "Running", and "stop" returns IdleState.new(). The hero, the context, keeps the current state and asks it what to do:
# 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 -> IdleThe same action means different things in different states: "stop" does nothing while idle and stops a running hero. Each rule lives in the class of its state, so a new state, like jumping, is one new class.
The states return the next state instead of storing the hero. A state that kept the hero while the hero keeps the state would be a reference cycle, and neither object would ever be freed.
Challenge
EasyA door is Locked, Closed or Open. DoorState (door_state.gd) declares get_state_name() and handle(action), and LockedState (locked_state.gd) is written: unlock makes the door Closed. Write:
ClosedState.handle():openreturns a newOpenState,locka newLockedState, anything elseself.OpenState.handle():closereturns a newClosedState, anything elseself.Door.handle_action(action): asks the current state for the next one. If it is the same object, printACTION: still STATE; otherwise printACTION: OLD -> NEWand switch to the new state.
The door starts Locked. The supplied code reads how many actions follow, then one action per line, and prints the final state. For open, unlock, open, lock the output is:
open: still Locked
unlock: Locked -> Closed
open: Closed -> Open
lock: still Open
Final state: OpenYour code goes in door.gd, closed_state.gd and open_state.gd. main.gd holds the supplied input/output code and cannot be edited.
Try it yourself
extends Node
func _ready():
# Supplied input/output code: keep it as it is
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())
This lesson includes a short quiz. Start the lesson to answer it and track your progress.
All lessons in Object Oriented Programming
1Fundamentals of OOP
External FilesIntroduction to OOPClasses vs ObjectsThe self KeywordMethodsPropertiesConstructor (_init)Recap - Simple Calculator4Polymorphism
Method Overriding RevisitedDuck TypingAbstract Classes and MethodsInterface DesignRecap - Shape Calculator7Signals
What is a Signal?Declaring and EmittingConnecting to SignalsSignals with ArgumentsRecap - Health System10Design Patterns Part 2
Command PatternAdapter PatternDecorator PatternTemplate Method PatternState PatternComposite Pattern2Class Properties
Instance vs Static VariablesStatic FunctionsProperty SettersProperty GettersPrivate by ConventionRecap - Bank Account Manager5Encapsulation
Public and Private MembersValidation with SettersInformation HidingRead-Only PropertiesRecap - Student Records System8Advanced OOP Concepts
Composition vs InheritanceInner ClassesNodes as ComponentsRefCounted vs NodeEnums in Classes11Project: Library Management
Project OverviewBook and User Classes3Inheritance
Basic InheritanceThe super KeywordMethod OverridingInheritance ChainsType Checks with isRecap - Employee Hierarchy6Special Methods
Virtual Methods IntroductionThe _to_string MethodCustom IteratorsRecap - Custom List9Design Patterns Part 1
Intro to Design PatternsSingleton PatternFactory PatternObserver PatternStrategy PatternPractice on your own: Online GDScript compiler