Красной нитью через все новогодние праздники у меня прошла тема путешествий во времени: в будущее, прошлое и "может быть". Началось все с уже традиционного рождественского Доктора, в новый год мы решили вместо Иронии судьбы посмотреть It's a Wonderful Life, очень доброе кино, в котором главного героя довели до ручки, он хотел покончить жизнь самоубийством, но добрый ангел показал ему "альтернативную вселенную", то, каким был бы мир, если бы главный герой вообще не появился на свет. Увидев этот мир, он сразу понял, что жизнь его вовсе не лишена смысла.
И самое главное, я писал новое, древовидное undo/redo, в котором действия, совершенные "в прошлом" не стирают всю последующую историю, а создают новую, альтернативную вселенную, но при желании всегда можно вернуться во вселенную старую, она никуда не девается.
(нажмите для увеличения)
Неожиданный побочный эффект: под действием этой библиотеки я и сам несколько выпал из времени: 30-го, пока на работе все пьянствовали, я писал код. 31-го, когда народ готовился к встрече нового года, я сидел на работе и писал код. А в 6 вечера 31-го числа меня неожиданно перенесло в темную параллельную вселенную, в которой я так и не доделал "средство от простуды автомобиля" УПЗА (Устройство Поддержания Заряда Аккумулятора), поэтому завестись не смогли, более чем за месяц бездействия аккумулятор сел в ноль...
Даже прикуривание не помогло, а старый акк. на 55Ач, стоявший дома, подключенный к UPS'у вместо уже дохлого родного 7Ач, тоже не смог завести мотор, так как уже изрядно потерял и емкость, и пусковый ток. Но все закончилось благополучно: аккумулятор из машины я принес домой, поставил на заряд током около 8 ампер. Через 1.5 часа немножко подзаряженный и согревшийся акк. попробовали вернуть на место, моментально завелись и успели к дедушке с бабушкой вовремя, встретили новый год вместе.
Библиотеку же до рабочего состояния я довел только сегодня.
Удивительно, однако до сих пор кибернетика в долгу перед народом. В большинстве программ undo/redo могут работать лишь до тех пор, пока программа открыта. Закрыли ее, загрузили файл проекта - все, пиши пропало, история сбросилась.
Мало того, операция undo оказывается чуть ли не самой опасной. Научно-фантастические фильмы учат нас, что с машиной времени шутки плохи, можно ненароком убить своего дедушку или раздавить бабочку, и начинаются проблемы - в лучшем случае просто перестаешь существовать (только для Фрая вселенная делает исключение), а в худшем - пространственно-временной парадокс, уничтожающий вселенную.
Undo-та же машина времени, возвращающая нас в прошлое документа. Любое сколь угодно маленькое действие стирает все, что мы отменили: попадая в прошлое, мы теряем настоящее. Особенно меня поражает Autocad, где даже прокрутка окна или изменение масштаба считается командой и потому затирает все отмененные действия!
Поэтому первая эйфория от кнопки undo быстро сменяется страхом: ее используют только для того, чтобы отменить несколько последних, заведомо ошибочных действий, и не более того, ведь если отправиться далеко в прошлое, возвращаться будет уже некуда... Вместо системы undo/redo люди начинают просто чаще сохраняться в разные места, скажем, диплом, диплом-1, диплом-2, диплом_финал, диплом_совсем_финал, диплом_111 и пр. В этих файлах очень легко потом запутаться - где именно была самая свежая версия - и начинается веселье.
Следующий этап - системы контроля версий, git, svn и иже с ними. Они очень хороши для текстовых файлов, но для документов другого формата все хуже: если формат сжатый, понять, чем документы различаются они не смогут, напишут лишь, differ, а даже хранить придется каждую версию отдельно.
Не спорю, хорошая вещь, особенно, когда множество людей работают над одним проектом, но все-таки это уже из пушки по воробьям, когда речь идет об одном файле, над которым работает один пользователь.
Вот что я предлагаю, хотя идея и не нова. Нужно довести систему undo/redo до ума, чтобы путешествия во времени и по альтернативным вселенным стали обыденными и не страшными. В первую очередь, информация не должна пропадать, вообще никакая. Отменили несколько действий, начали делать какие-то другие - пошло ответвление от первоначального хода событий. Не понравилось - открыли журнал и перенеслись в любое состояние, какое душе угодно. Кроме того, в дерево можно будет писать свои комментарии, где что было сделано.
Переход от линейного undo к древовидному на удивление прост! Не нужно никакого дублирования данных, создания множества копий, не нужно даже переписывать код команд (классов, производных от AbstractCommand, с методами Execute() и Undo()), меняется лишь структура данных, в которые они объединены. Возвращаясь к Доктору: булева переменная, указывающая, куда идет активная ветвь, называется TurnLeft.
И места много не требуется, ведь каждая команда хранит в себе лишь ту информацию, которая необходима, чтобы прокрутить состояние в нужную сторону. Скажем, команда "загрузить изображение", когда она не выполнена, хранит в себе изображение для загрузки. После выполнения хранить его повторно уже не требуется, от команды останется лишь заголовок. Видя этот заголовок, мы можем элементарно выполнить undo, просто "взять его под крылышко" команды, обнулив указатели на него в документе. Даже перемещать изображение в памяти не нужно, оно остается на месте, просто все о нем забывают, кроме нашей команды. "Помни!".
Команда обрезки изображения перед выполнением хранит лишь 4 числа, координаты прямоугольника, а после выполнения еще и "обрезки". Разумеется, это достаточно весомо, но ничего лишнего, уж точно не образ документа на каждом шаге!
Пока я "потренировался" на своей многострадальной
ImageGraph2Txt, добавив в нее еще и древовидный undo, хоть она могла бы без него спокойно обойтись! Вот
ссылка для скачивания, можно поиграться. С интерфейсом пока еще не все хорошо, история открывается в отдельном окне и легко теряется, но можно ее просто снова открыть, "правка->журнал" или в выпадающем меню undo/redo.
Кстати, погуглил, есть ли еще программы с древовидным undo. Оказывается есть, целых 2 штуки, это Vim и Emacs. Большего сказать не могу, я их боюсь!
Все время меня несет куда-то пофилософствовать, каково это - жить в мире с альтернативными вселенными. Человек трагически погиб, мы вернулись в прошлое и предотвратили его гибель, но эти события никуда не исчезли, они по-прежнему есть. А может, и возвращаться не нужно было, а этих вселенных с самого начала целый мешок и они плодятся каждую секунду? И ведь нельзя сказать, что это совсем другой человек, из своей реализации вижу - тот же самый! Нет никакой хитрой процедуры по "клонированию" вселенной и всех живущих в ней людей, они остаются в одном экземпляре, просто время больше не является прямой линией. Все, что нужно для такой реализации - это работа только с обратимыми операторами.
И вопрос ко всем: какую программу вы хотели бы видеть с древовидным undo?
Очень запоздало, с новым годом всех и с рождеством (если данный интерфейс поддерживается)!