Страница 1 из 2

Есть какие то типовые варианты создания функции отмена?

СообщениеДобавлено: 29.04.2015 12:53:36
Sharfik
Функция Отмена последних действий иногда очень полезная, но понятия не имею какими методами ее можно организовать в программах.
Если вся программа построена на классах наследованных от TObject например, то в каждом случае надо самому все реализовывать или есть какие то средства для "сливания" предыдущих значений памяти в хранилище отдельное?

Re: Есть какие то типовые варианты создания функции отмена?

СообщениеДобавлено: 29.04.2015 13:00:39
MiniQ
Реализовывать нужно самому.
Например хранить отдельно "старые" объекты и новые.

Re: Есть какие то типовые варианты создания функции отмена?

СообщениеДобавлено: 29.04.2015 13:10:33
Sharfik
MiniQ писал(а):Например хранить отдельно "старые" объекты и новые.

Я что то зациклился на мысли о хранении значения переменных, полностью отдельно объект и правда проще слить. Спасибо.

Re: Есть какие то типовые варианты создания функции отмена?

СообщениеДобавлено: 29.04.2015 13:50:38
zub
Я стараюсь хранить минимально возможный набор данных для отката - сохранять целый объект накладно, особенно при возможности массированых изменений. Вообще тема сложная и индивидуальная - единого решения нет.
Совет - undo\redo нужно вводить какможно раньше (ну или хотябы продумать как оно будет), внедрять это хозяйство в уже рабочий код - та еще каторга

Re: Есть какие то типовые варианты создания функции отмена?

СообщениеДобавлено: 29.04.2015 16:20:43
stanilar
Есть такое слово, как сериализация, оно поддерживается наследниками TPersistent.
Если логика работы представлена в виде дерева наследников TPersistent, которые пишут лог своих изменений, то возможно построение функции записи макроса работы программы, и макроса текущего состояния.

Сам подумываю над таким компонентом.

Re: Есть какие то типовые варианты создания функции отмена?

СообщениеДобавлено: 30.04.2015 01:23:22
Mirage
Есть паттерн, сильно облегчающий реализацию undo/redo, а также дающий доп. плюшки:
Каждый тип операции модификации данных (коих по идее немног - изменение свойств чего-то, модификация данных и т.п.) оборачивается в класс с одним методом - Perform() допустим.
Первый его вызов совершает модификацию данных, второй - отменяет. Третий опять совершает и т.д.
Как это делается - сериализацией или как-то по-другому уже другой вопрос. Зависит.
И да, желательно сразу так делать, хотя, если программа написана вменяемо, то выделить классы операций должно быть несложно и уже существующем коде.

Re: Есть какие то типовые варианты создания функции отмена?

СообщениеДобавлено: 30.04.2015 11:54:15
zub
>>оборачивается в класс с одним методом - Perform() допустим.
не все операции одинаково выглядят в ундо\редо, такчто метода лучше 2

Re: Есть какие то типовые варианты создания функции отмена?

СообщениеДобавлено: 30.04.2015 12:00:03
MiniQ
Mirage писал(а):Первый его вызов совершает модификацию данных, второй - отменяет. Третий опять совершает и т.д.

Что-то мудрено. А если мне нужно взглянуть, как было 10 операций назад? Т.е. откатить назад 10 операций и потом вернуть все в текущее состояние.
Вот этот патерн "первый вызов, второй вызов" как будет работать?

Re: Есть какие то типовые варианты создания функции отмена?

СообщениеДобавлено: 30.04.2015 12:42:59
Лекс Айрин
MiniQ писал(а):Что-то мудрено. А если мне нужно взглянуть, как было 10 операций назад?


Думаю, проще сделать ссылку на следующее/предыдущее изменение. (то есть, использовать список операций)

Re: Есть какие то типовые варианты создания функции отмена?

СообщениеДобавлено: 30.04.2015 14:26:04
ViTality
помница когда писал свой текстовый супер редактор с БЖиШ везде советовали TCollection

Re: Есть какие то типовые варианты создания функции отмена?

СообщениеДобавлено: 01.05.2015 02:16:27
Mirage
zub писал(а):не все операции одинаково выглядят в ундо\редо, такчто метода лучше 2


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

MiniQ писал(а):А если мне нужно взглянуть, как было 10 операций назад? Т.е. откатить назад 10 операций и потом вернуть все в текущее состояние.Вот этот патерн "первый вызов, второй вызов" как будет работать?


Собственно, для этого и делается. При применении операции вызываем Perform() и кладем её в стек. При отмене - из стека достаем, кладем в другой стек и снова вызываем Perform(). При повторении (redo) достаем из второго стека, применяем, кладем в первый.
Можно и 10 шагов откатить и накатить обратно.
Еще оба стека красиво в один массив ложатся, с индексом текущей операции. Массив просто обрезается по текущему индексу при добавлении новой операции.

Re: Есть какие то типовые варианты создания функции отмена?

СообщениеДобавлено: 01.05.2015 10:16:46
zub
А какие проблемы могут быть с двойным вазовом? при работе не с "командой", а со стеком это сделать не получится, даже если постараться

>>Еще оба стека красиво в один массив ложатся, с индексом текущей операции. Массив просто обрезается по текущему индексу при добавлении новой операции.
у меня в конечном итоге так и получилось

Re: Есть какие то типовые варианты создания функции отмена?

СообщениеДобавлено: 02.05.2015 01:22:19
Mirage
zub писал(а): а со стеком это сделать не получится, даже если постараться


Почему это не получится? Достал экземпляр операции из стека и вызывай её метод сколько хочешь раз.

Re: Есть какие то типовые варианты создания функции отмена?

СообщениеДобавлено: 02.05.2015 01:53:23
zub
Если так рассуждать то можно хоть что сделать)) Множественный вызов Perform тоже ничего хорошего не сулит.
Имхо ниче доставать ненадо, надо просто делать UndoStack.Undo или UndoStack.Redo. Возня с "операцией" должна быть только на этапе ее создания и подготовки, после помещения в стек нечего к ней лезти.

Как ни крути а Perform будет универсальным (с одинаковой реализацией ундо\редо) только для простых случаев изменения данных. Например добавление\удаление элемента массива уже не такая универсальная операция.

Re: Есть какие то типовые варианты создания функции отмена?

СообщениеДобавлено: 04.05.2015 03:32:18
Mirage
zub писал(а):Множественный вызов Perform тоже ничего хорошего не сулит.


Это как раз штатная ситуация. Потерь данных точно нет. В отличии от повторного вызова Undo.

zub писал(а):Имхо ниче доставать ненадо, надо просто делать UndoStack.Undo или UndoStack.Redo. Возня с "операцией" должна быть только на этапе ее создания и подготовки, после помещения в стек нечего к ней лезти.


Можно, конечно, коллекцию (стек) нагрузить знаниями о том, что там хранятся операции и что их нельзя отдавать, и что надо контролировать чтобы они правильно вызывались. Но это как раз не "просто делать". Тут прям напрячься надо. :lol:

zub писал(а):Как ни крути а Perform будет универсальным (с одинаковой реализацией ундо\редо) только для простых случаев изменения данных. Например добавление\удаление элемента массива уже не такая универсальная операция.


То, что он универсальный это не принципиальный момент. Может и не быть универсальным. Может хранить флажок, что делаем в следующий раз: добавляем или удаляем.
Принципиально то, что все максимально просто, вся информация в самой операции и как её не мучай, потери данных не будет.
А коллекция останется коллекцией. Её можно даже будет в любой момент заменить на другую с аналогичным интерфейсом, как и должно быть с коллекциями.