命令模式
本课属于 Coddy GDScript 学习旅程的“面向对象编程”单元。第 50 课,共 65 课。
Command 模式会将一个操作转换为对象。这个对象会存储运行该操作所需的一切内容,这样你就可以把它保存在列表中,以后再运行,或者撤销它。
每个命令都有两个相同的方法,它们由一个抽象父类声明:
# command.gd
@abstract
class_name Command
extends RefCounted
@abstract func execute() -> void
@abstract func undo() -> void具体命令会保存它作用的对象(称为接收者)以及操作的数据。这里的接收者是一个 Hero,它有一个 var x := 0:
# move_command.gd
class_name MoveCommand
extends Command
var hero: Hero
var dx: int
func _init(target: Hero, step: int) -> void:
hero = target
dx = step
func execute() -> void:
hero.x += dx
func undo() -> void:
hero.x -= dx调用者运行命令并记住它们。它不需要知道命令的作用:
# command_history.gd
class_name CommandHistory
extends RefCounted
var done: Array[Command] = []
func run(command: Command) -> void:
command.execute()
done.append(command)
func undo_last() -> void:
if done.is_empty():
return
var last: Command = done.pop_back()
last.undo()# main.gd
var hero := Hero.new()
var history := CommandHistory.new()
history.run(MoveCommand.new(hero, 3))
history.run(MoveCommand.new(hero, 2))
print(hero.x)
history.undo_last()
print(hero.x)5
3历史记录是一个栈:pop_back() 会取出最新的命令,因此撤销会按相反的顺序回退操作。同一批对象还能让你重新绑定按键(将按键映射到命令)、重放(再次运行已保存的列表)和排队回合。
引用是单向的:命令指向英雄,而英雄对它们一无所知。如果英雄也存储了自己的命令,这些对象就会彼此保持存活,永远不会被释放。
挑战
简单英雄在网格上移动,并且每一步都可以撤销。Command(command.gd)声明了 execute() 和 undo(),Hero(hero.gd)有 x、y 和 position_text()。请编写:
MoveCommand:构造函数保存英雄以及步数step_x和step_y。execute()将步数加到英雄的x和y上,undo()则将它们减去。CommandHistory:run(command)执行命令并保存它。undo_last()撤销最新保存的命令,将其移除,并返回true;如果没有保存任何命令,则返回false。
提供的代码会读取后续操作的数量,然后逐行读取操作(up、down、left、right 或 undo),并在每次操作后打印位置。up 会让 y 减少 1。对于 right、up、undo、undo、undo,输出为:
right -> (1, 0)
up -> (1, -1)
undo -> (1, 0)
undo -> (0, 0)
Nothing to undo你的代码应写在 move_command.gd 和 command_history.gd 中。main.gd 中包含提供的输入/输出代码,不能编辑。
亲手试试
extends Node
func _ready():
# 已提供的输入和输出代码:保持原样。
var hero := Hero.new()
var history := CommandHistory.new()
var steps := {"up": [0, -1], "down": [0, 1], "left": [-1, 0], "right": [1, 0]}
var count := int(OS.read_string_from_stdin())
for i in count:
var action := OS.read_string_from_stdin()
if action == "undo":
if history.undo_last():
print("undo -> ", hero.position_text())
else:
print("Nothing to undo")
else:
var step: Array = steps[action]
history.run(MoveCommand.new(hero, step[0], step[1]))
print(action, " -> ", hero.position_text())
这节课包含一个小测验。开始学习即可作答,并记录你的进度。
面向对象编程中的所有课
自己练习: GDScript 在线编译器