状态模式
本课属于 Coddy GDScript 学习旅程的“面向对象编程”单元。第 54 课,共 65 课。
State 模式会为对象可能处于的每种模式分别提供一个状态对象。对象会把工作交给当前状态处理,因此状态改变时,它的行为也会改变。
对于少数几个状态,枚举和 match 就够用了。当每个状态都会以自己的方式响应多个操作时,match 就会随着状态和操作的增加而变长。状态模式将每个状态的规则放入各自的类中:
# hero_state.gd
@abstract
class_name HeroState
extends RefCounted
@abstract func get_state_name() -> String
@abstract func handle(action: String) -> HeroStatehandle() 会返回下一个状态:要切换到的新状态对象,或者返回 self 以保持当前状态。
# 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 的写法也一样:它的名称是 "Running",而 "stop" 会返回 IdleState.new()。英雄,也就是上下文,会保存当前状态,并询问它该怎么做:
# 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同一个操作在不同状态下有不同含义:空闲时,"stop"不起作用;英雄正在运行时,它会让英雄停下来。每条规则都属于对应状态的类,因此新增一个状态,比如跳跃,只需要新增一个类。
状态会返回下一个状态,而不是存储英雄对象。如果一个状态持有英雄对象,而英雄对象又持有该状态,就会形成引用循环,两个对象都不会被释放。
挑战
简单门的状态可以是 Locked、Closed 或 Open。DoorState(door_state.gd)声明了 get_state_name() 和 handle(action),LockedState(locked_state.gd)已经编写完成:unlock 会让门变为 Closed。请编写:
ClosedState.handle():open返回新的OpenState,lock返回新的LockedState,其他情况返回self。OpenState.handle():close返回新的ClosedState,其他情况返回self。Door.handle_action(action):向当前状态询问下一个状态。如果是同一个对象,就输出ACTION: still STATE;否则输出ACTION: OLD -> NEW,并切换到新状态。
门初始状态为 Locked。提供的代码会读取后续操作的数量,然后逐行读取操作,并输出最终状态。对于 open、unlock、open、lock,输出为:
open: still Locked
unlock: Locked -> Closed
open: Closed -> Open
lock: still Open
Final state: Open你的代码应写在 door.gd、closed_state.gd 和 open_state.gd 中。main.gd 存放提供的输入/输出代码,不能编辑。
亲手试试
extends Node
func _ready():
# 已提供的输入输出代码:保持原样
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())
这节课包含一个小测验。开始学习即可作答,并记录你的进度。
面向对象编程中的所有课
自己练习: GDScript 在线编译器