Реализация Многоуровневого Функционала Отмены/Повтора На Примере Прототипа Электронной Таблицы.



Введение Кнопки «Отменить» и «Повторить», позволяющие отменить и вернуть назад любые действия пользователя, а также просмотреть список всех выполненных действий, являются стандартом де-факто для таких приложений, как текстовые процессоры и среды разработки, графики и Редакторы САПР, системы редактирования и редактирования звука и видео.

Они настолько привычны пользователю, что последний воспринимает их наличие как данность, всего лишь одну функциональность среди десятков других.

Но с точки зрения разработчика требование отмены является одним из факторов, влияющих на всю архитектуру проекта, определяемых на самых ранних стадиях проекта разработки.



Реализация многоуровневого функционала отмены/повтора на примере прототипа электронной таблицы.
</p><p>

Существует довольно много информации из открытых источников о том, как практически реализовать функцию отмены/повтора.

Классическая книга? Гаммы и т. д. «Техники объектно-ориентированного программирования.

Шаблоны проектирования" кратко упоминает пригодность шаблона «команда» для этой цели; В Интернете много общей информации по этой теме, но нам не удалось найти достаточно полного, проработанного примера реализации.

В нашей статье мы попытаемся восполнить этот пробел и на основе опыта автора продемонстрировать подробный пример архитектуры приложения, поддерживающей отмену/повтор, который можно использовать как основу для других проектов.

Примеры кода в статье приведены на Java, но в них нет ничего Java-специфичного и все представленные здесь идеи подходят для любого объектно-ориентированного языка (сам автор впервые реализовал их в Delphi).

Следует отметить, что для разных нужд и типов приложений существуют разные «модели отмены»: линейные — с отменой операций строго в обратном порядке, и нелинейные — с отменой произвольных операций из истории выполненных действий.

.

Мы поговорим о реализации линейная отмена в системе с синхронизированными изменениями модели данных , т. е.

такой, в котором не допускается одновременное изменение внутреннего состояния модели данных в разных потоках выполнения.

Приведена классификация возможных моделей отмены, например: в статье в Википедии .

Мы естественно предполагаем, что в приложении реализовано разделение модели данных (Model) от представления (View), и функциональность отмены реализована на уровне модели данных , в виде методов отменить() И повторить() один из ее занятий.



Показательный пример

В качестве наглядного примера в статье рассматривается модель данных приложения, прототипирующего электронную таблицу (стиль MS Excel/LibreOffice Calc).

Имеется лист (для простоты только один), состоящий из ячеек, значения и размеры которых можно менять, менять местами строки и столбцы, и все эти действия, соответственно, отменяемы.

Исходные коды и соответствующие модульные тесты доступны по адресу https://github.com/inponomarev/undoredo и может быть скомпилирован и выполнен с использованием Maven. Основными объектами в нашем примере являются:

  1. рабочий лист - класс Рабочий лист ,
  2. строки и столбцы — классы Ряд И Столбец (наследники класса Элемент оси ),
  3. ячейки - класс Клетка .

Получившаяся простая модель данных изображена на диаграмме классов на рисунке ниже:

Реализация многоуровневого функционала отмены/повтора на примере прототипа электронной таблицы.
</p><p>

Не вдаваясь в подробности реализации электронных таблиц, кратко отметим основные принципы структуры исходного кода.

Хотя для пользователя электронная таблица предстает как двумерный массив данных, границы которого выходят далеко за пределы экрана, использование двумерного массива для модели данных не оправдано и с точки зрения потребления оперативной памяти.

или с точки зрения скорости типовых операций.

Например, если в более ранних версиях MS Excel допускалось существование 65536 столбцов и строк, то выделение памяти на 65536 2 , то есть 4 миллиарда ячеек, было бы просто технически невозможно в 32-битной системе.

Решение состоит в том, чтобы использовать древовидные словари и хеш-таблицы для хранения только измененных значений, заменяя отсутствующее значение в словаре значением по умолчанию.

Для хранения копий Ряд И Столбец , используются словари ДеревоКарта ряды И ДеревоКарта столбцы в классе Рабочий лист .

Для хранения копий Клетка используется словарь Хэшмап клетки в классе Ряд .

Значения этой хеш-таблицы являются ссылками на объекты Клетка , а ключи — это объекты столбцов.

Такой подход к хранению данных позволяет найти оптимальный баланс между производительностью и объемом памяти, используемой для всех практически необходимых операций над контентом.

Рабочий лист .



Корневой класс модели и отменяемые методы

Сорт Рабочий лист в нашем примере является центральным: 1) работа со всеми остальными объектами бизнес-логики начинается с получения экземпляра именно этого класса, 2) экземпляры других классов могут работать только в контексте объекта Рабочий лист , 3)через метод сохранять(.

) и статический метод нагрузка(.

) он сохраняет в поток и восстанавливает из потока состояние всей системы.

Мы назовем этот класс корневой класс модели .

Обычно при разработке приложений в архитектуре Модель-Представление-Контроллер не возникает затруднений с определением корневого класса модели.

Именно ему предоставляются методы, специфичные для функциональности Undo/Redo. Также не составит труда определить методы, изменяющие состояние модели .

Это методы, результаты вызова которых необходимо отменить с помощью отмены.

В нашем примере следующее:

  • setCellValue (int row, int col, String value) — устанавливает значение ячейки (для простоты примера мы предполагаем, что ячейки могут принимать только строковые значения!),
  • InsertValues(int top, int left, значение String[][]) — вставляет в ячейки значения двумерного массива (например, полученные из буфера обмена),
  • setRowHeight (целая строка, целая высота) , setColWidth (int col, int ширина) — задайте высоту строки и ширину столбца,
  • InsertColumnLeft (интервал colNum) , вставитьRowAbove (int rowNum) - вставлять столбцы и строки,
  • deleteColumn (интервал colNum) , deleteRow (интервал rowNum) - удалять столбцы и строки,
  • moveColumn(int from, int to) , moveRow(интервал от, интервал до) — перемещать столбцы и строки вместе с их содержимым, заменяя содержимое конечного столбца/строки.

В реальном приложении, конечно, их может быть гораздо больше.

Каждому из них нужно связать соответствующий класс команд (о чем речь пойдет позже), а методы нужно переписать особым образом.



Стеки отмены и повтора

В линейной модели отмены операции отмены выполняются таким образом, чтобы сохранить последовательность действий, выполняемых над документом.

Например, если в документ сначала был добавлен столбец, а затем была изменена его ширина, то отмена этих операций возможна только в обратном порядке, а возврат — в прямом.

Поэтому для хранения операций, подлежащих отмене и восстановлению, естественно использовать два стека в связанных списках, которые являются частью корневого класса модели.

При вызове метода, изменяющего состояние модели, стек Redo сбрасывается, а стек Undo пополняется следующим значением.

Выполнение команды «Отменить» должно привести к извлечению значения из стека «Отменить» и помещению в стек «Повторить».

Выполнение команды Redo, если оно произойдет, должно снова вернуть значение в стек Undo (см.

рисунок).



Реализация многоуровневого функционала отмены/повтора на примере прототипа электронной таблицы.
</p><p>

Содержимое этих стеков является объектами, производными от класса.

Команда , о котором речь пойдет ниже.

Вот список общедоступных методов корневого класса бизнес-логики, которые обеспечивают доступ к функциям отмены/повтора:

  • отменить() — отмена выполняемого действия путем вызова любого метода, изменяющего состояние модели, и перемещения его в стек Redo:
       

    Command cmd = undoStack.pop();

Теги: #ООП #шаблоны проектирования #отмена #отмена #java #проектирование и рефакторинг #ООП
Вместе с данным постом часто просматривают:

Автор Статьи


Зарегистрирован: 2019-12-10 15:07:06
Баллов опыта: 0
Всего постов на сайте: 0
Всего комментарий на сайте: 0
Dima Manisha

Dima Manisha

Эксперт Wmlog. Профессиональный веб-мастер, SEO-специалист, дизайнер, маркетолог и интернет-предприниматель.