Паттерн «Команда»
Часть раздела Объектно-ориентированное программирование путешествия по 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() извлекает самую новую команду, поэтому отмена проходит действия в обратном порядке. Те же объекты позволяют переназначать клавиши (клавиша соответствует команде), повторно запускать сохранённый список и создавать очереди ходов.
Ссылки направлены в одну сторону: команды указывают на героя, а герой ничего о них не знает. Если бы герой тоже хранил свои команды, объекты поддерживали бы жизнь друг друга и никогда не освобождались.
Задание
ЛегкоГерой перемещается по сетке, и каждое перемещение можно отменить. 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())
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Объектно-ориентированное программирование
1Основы ООП
Внешние файлыВведение в ООПКлассы и объектыКлючевое слово selfМетодыСвойстваКонструктор (_init)Повторение: простой калькулятор4Полиморфизм
Переопределение методов: повторениеУтиная типизацияАбстрактные классы и методыПроектирование интерфейсовПовторение — калькулятор фигур7Сигналы
Что такое сигнал?Объявление и отправкаПодключение к сигналамСигналы с аргументамиПовторение — система здоровья10Паттерны проектирования. Часть 2
Паттерн «Команда»Паттерн «Адаптер»Паттерн «Декоратор»Паттерн «Шаблонный метод»Паттерн «Состояние»Паттерн «Компоновщик»2Свойства классов
Переменные экземпляра и классаСтатические функцииСеттеры свойствГеттеры свойствПриватность по соглашениюПовторение — менеджер банковского счёта5Инкапсуляция
Открытые и закрытые членыПроверка данных с помощью сеттеровСокрытие информацииСвойства только для чтенияПовторение — система учета студентов8Продвинутые концепции ООП
Композиция и наследованиеВнутренние классыУзлы как компонентыRefCounted и NodeПеречисления в классах11Проект: Управление библиотекой
Обзор проектаКлассы книг и пользователей3Наследование
Основы наследованияКлючевое слово superПереопределение методовЦепочки наследованияПроверка типов с помощью isПовторение — иерархия сотрудников6Специальные методы
Введение в виртуальные методыМетод _to_stringПользовательские итераторыПовторение: пользовательский список9Паттерны проектирования. Часть 1
Введение в паттерны проектированияПаттерн «Одиночка»Паттерн «Фабрика»Паттерн «Наблюдатель»Паттерн «Стратегия»Потренируйтесь самостоятельно: Онлайн-компилятор GDScript