Menu
CoddyTech

תבנית הפקודה

חלק מהיחידה תכנות מונחה עצמים במסלול ה-GDScript של Coddy. שיעור 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() שולפת את הפקודה החדשה ביותר, כך שביטול הפעולות עובר עליהן לאחור, בסדר הפוך. אותם אובייקטים מאפשרים לך גם למפות מחדש מקשים (מקש משויך לפקודה), להפעיל שוב רצפי פעולות (להריץ שוב את הרשימה השמורה) ולנהל תורים של תורות.

ההפניות הן חד־כיווניות: הפקודות מצביעות על הגיבור, והגיבור לא יודע עליהן דבר. אם הגיבור היה שומר גם את הפקודות שלו, האובייקטים היו מחזיקים זה את זה בחיים ולא היו משתחררים לעולם.

challenge icon

אתגר

קל

גיבור נע על גבי רשת, וניתן לבטל כל תנועה. 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 באחד. עבור 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())
quiz iconבחנו את עצמכם

השיעור הזה כולל חידון קצר. התחילו את השיעור כדי לענות עליו ולעקוב אחרי ההתקדמות.

כל השיעורים ביחידה תכנות מונחה עצמים

תרגלו בעצמכם: קומפיילר GDScript אונליין