Список истории
Многоуровневый откат и повтор: UNDO-REDO
Поддержка отката произвольной глубины и сопровождающего его повтора представляет прямое расширение рассмотренной нами схемы.
Что не позволяло нам производить откат на большую глубину? Ответ очевиден - у нас был только один объект - последний созданный экземпляр COMMAND, доступный через requested.
Фактически мы создавали столь много объектов, сколько команд выполнял пользователь. Но поскольку в нашем проекте присутствует только одна ссылка на командный объект - requested, всегда присоединенная к последней команде, то каждый командный объект становится недостижимым, как только пользователь создает новую команду. Нам нет необходимости заботиться о судьбе этих старых объектов. Важной частью, обеспечивающей элегантность и простоту хорошего ОО окружения, является сборщик мусора (см. лекцию 9 курса "Основы объектно-ориентированного программирования"), в задачу которого входит освобождение памяти. Было бы ошибкой пытаться самим использовать память, так как все объекты имеют разную структуру и размеры. |
Для обеспечения глубины отката достаточно заменить единственный объект requested списком, содержащим выполненные команды, - списком истории:
history: SOME_LIST [COMMAND]Имя SOME_LIST не является именем настоящего класса, - в подлинном ОО стиле АТД мы исследуем, какие операции и свойства необходимы классу SOME_LIST, и позже вынесем заключение, какой же списочный класс из базовой библиотеки (Base library) следует использовать. Принципиальные операции, нужные нам непосредственно, хорошо известны из предыдущего обсуждения:
Рис. 3.3.Список истории
· Put - команда вставки элемента в конец списка (единственное необходимое нам место вставки). По соглашению, putпозиционирует курсор списка на только что вставленном элементе.
· Empty - запрос определения пустоты списка.
· Before, is_first и is_last - запросы о позиции курсора.
· Back, forth - команды, передвигающие курсор назад, вперед на одну позицию.
· Item - запрос элемента в позиции, заданной курсором. Этот компонент имеет предусловие: (not empty) and (not before), которое можно выразить как запрос on_item.
В отсутствие откатов курсор всегда (за исключением пустого списка) будет указывать на последний элемент и is_last будет истинным. Если же пользователь начнет выполнять откат, курсор начнет передвигаться назад по списку вплоть до before, если отменяются все выполненные команды. Когда же начинается повтор, то курсор перемещается вперед.
На рис. 3.3 курсор указывает на элемент, отличный от последнего. Это означает, что пользователь выполнял откат, возможно, перемежаемый повторами. Заметьте, число команд Undo всегда не меньше числа Redo (в состоянии на рисунке оно на два больше). Если в этом состоянии пользователь выберет обычную команду (ни Undo, ни Redo) соответствующий элемент будет вставлен непосредственно справа от курсора. Это означает, что остававшиеся справа в списке элементы будут потеряны, так для них не имеет смысла выполнение Redo. Здесь возникает та же ситуация, которая привела нас в начале лекции к введению понятия операции Skip (см. У3.4). Как следствие, в классе SOME_LIST понадобится еще один компонент - процедураremove_all_right, удаляющий все элементы справа от курсора.
Выполнение Undo возможно, если и только если курсор стоит на элементе с истинным значением on_item. Выполнение Redoвозможно, если и только если был сделан откат, для которого еще не выполнена операция Redo, - это означает истинность выражения: (not empty) and (not is_last), которое будем называть запросом not_last.