Шаблон проектирования для отмены движка - PullRequest
114 голосов
/ 08 сентября 2008

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

Я уже кодировал механизм отмены, который сохраняет глубокую копию после каждой модификации модели. Теперь я начал думать, мог бы я по-другому кодировать. Вместо сохранения глубоких копий, я мог бы сохранить список каждого действия модификатора с соответствующим обратным модификатором. Чтобы я мог применить обратные модификаторы к текущей модели, чтобы отменить, или модификаторы, чтобы повторить.

Я могу представить, как вы выполняете простые команды, которые изменяют свойства объекта и т. Д. Но как насчет сложных команд? Как вставка новых объектов узлов в модель и добавление некоторых линейных объектов, которые сохраняют ссылки на новые узлы.

Как можно реализовать это?

Ответы [ 22 ]

86 голосов
/ 08 сентября 2008

Большинство примеров, которые я видел, используют для этого вариант Command-Pattern . Каждое отменяемое действие пользователя получает свой собственный экземпляр команды со всей информацией для выполнения действия и его отката. Затем вы можете сохранить список всех выполненных команд и откатить их одну за другой.

31 голосов
/ 18 февраля 2009

Я думаю, что и memento, и команда не практичны, когда вы имеете дело с моделью размера и объема, которую подразумевает OP. Они будут работать, но это будет много работы для поддержания и расширения.

Для такого типа проблемы, я думаю, вам нужно встроить поддержку вашей модели данных для поддержки дифференциальных контрольных точек для каждого объекта , участвующего в модели. Я сделал это один раз, и он работал очень гладко. Самое важное, что вам нужно сделать, - это избегать прямого использования указателей или ссылок в модели.

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

Реализовать отмену / повтор очень просто: выполните свое действие и установите новую контрольную точку; откатить все версии объекта до предыдущей контрольной точки.

Это требует некоторой дисциплины в коде, но имеет много преимуществ: вам не нужны глубокие копии, поскольку вы выполняете дифференциальное хранение состояния модели; Вы можете ограничить объем памяти, который вы хотите использовать ( очень , важный для таких вещей, как модели САПР), либо по количеству повторов, либо по используемой памяти; очень масштабируемый и не требующий обслуживания для функций, которые работают в модели, так как им не нужно ничего делать для реализации отмены / повтора.

17 голосов
/ 08 сентября 2008

Если вы говорите о GoF, шаблон Memento определенно обращается к отмене.

15 голосов
/ 08 сентября 2008

Как уже говорили другие, шаблон команды является очень мощным методом реализации Undo / Redo. Но есть важное преимущество, которое я хотел бы упомянуть в шаблоне команды.

При реализации отмены / повтора с использованием шаблона команды вы можете избежать большого количества дублирующегося кода путем абстрагирования (до некоторой степени) операций, выполняемых с данными, и использования этих операций в системе отмены / возврата. Например, в текстовом редакторе вырезать и вставить являются дополнительными командами (кроме управления буфером обмена). Другими словами, операция отмены вырезки - это вставка, а операция отмены вставки - вырезание. Это относится к гораздо более простым операциям, таким как набор и удаление текста.

Ключевым моментом здесь является то, что вы можете использовать вашу систему отмены / возврата в качестве основной системы команд для вашего редактора. Вместо написания системы, такой как «создать объект отмены, изменить документ», вы можете «создать объект отмены, выполнить операцию возврата для объекта отмены, чтобы изменить документ».

Теперь, по общему признанию, многие люди думают про себя: "Ну, да, разве это не часть концепции командования?" Да, но я видел слишком много систем команд, которые имеют два набора команд: один для немедленных операций, а другой для отмены / повтора. Я не говорю, что не будет команд, относящихся к немедленным операциям и отмене / повтору, но уменьшение дублирования сделает код более понятным.

8 голосов
/ 08 сентября 2008

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

-Adam

7 голосов
/ 08 сентября 2008

Это может быть случай, когда применяется CSLA . Он был разработан для обеспечения сложной поддержки отмены объектов в приложениях Windows Forms.

6 голосов
/ 08 сентября 2008

Я успешно реализовал сложные системы отмены, используя шаблон Memento - очень легко, и имеет преимущество, естественно, также предоставляя платформу Redo. Более тонкое преимущество заключается в том, что совокупные действия могут содержаться и в одной отмене.

В двух словах, у вас есть две стопки объектов сувениров. Один для отмены, другой для повторения. Каждая операция создает новый сувенир, который в идеале будет вызывать изменение состояния вашей модели, документа (или чего-либо еще). Это добавляется в стек отмены. Когда вы выполняете операцию отмены, в дополнение к выполнению действия «Отменить» для объекта Memento для повторного изменения модели, вы также извлекаете объект из стека отмены и помещаете его прямо в стек повторения.

Каким образом реализуется метод изменения состояния вашего документа, полностью зависит от вашей реализации. Если вы можете просто выполнить вызов API (например, ChangeColour (r, g, b)), то перед ним следует запрос, чтобы получить и сохранить соответствующее состояние. Но шаблон также будет поддерживать создание глубоких копий, снимков памяти, создание временных файлов и т. Д. - все зависит от вас, поскольку это просто реализация виртуального метода.

Для выполнения агрегатных действий (например, пользователь Shift-выбирает загрузку объектов для выполнения операций, таких как удаление, переименование, изменение атрибута), ваш код создает новый стек отмены в виде отдельного сувенира и передает его фактическая операция для добавления отдельных операций. Таким образом, ваши методы действий не должны (а) иметь глобальный стек, о котором нужно беспокоиться, и (б) могут быть закодированы одинаково, независимо от того, выполняются ли они изолированно или как часть одной агрегатной операции.

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

5 голосов
/ 08 сентября 2008

Только что читал о шаблоне команд в моей книге по гибкой разработке - может, в этом есть потенциал?

Вы можете заставить каждую команду реализовывать командный интерфейс (который имеет метод Execute ()). Если вы хотите отменить, вы можете добавить метод Undo.

подробнее здесь

4 голосов
/ 08 сентября 2008

Я с Мендель Зибенга о том, что вы должны использовать шаблон команды. Вы использовали паттерн Memento Pattern, который со временем может стать очень расточительным.

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

Я бы посоветовал вам проверить, есть ли фреймворк, который уже создал модель для отмен на выбранном вами языке программирования / фреймворке. Приятно изобретать новые вещи, но лучше взять что-то уже написанное, отлаженное и протестированное в реальных сценариях. Было бы полезно, если бы вы добавили то, что вы пишете, чтобы люди могли порекомендовать известные им фреймворки.

3 голосов
/ 30 июня 2009

Проект Codeplex :

Это простая структура для добавления функциональности Undo / Redo в ваши приложения на основе классического шаблона проектирования Command. Он поддерживает действия слияния, вложенные транзакции, отложенное выполнение (выполнение при фиксации транзакции верхнего уровня) и возможную нелинейную историю отмен (где вы можете выбрать несколько действий для повторения).

...