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