Создать историю состояний в Java - PullRequest
5 голосов
/ 03 февраля 2009

Мне нужно сохранить историю состояний по нескольким действиям в приложении Java, которые я могу позже перезагрузить, чтобы восстановить состояние при определенном действии. Другими словами, у меня есть экран, с которым связано состояние, и мне нужно сохранить его, а также любые изменения в истории, чтобы я мог восстановить состояние экрана в любое время. Это похоже на «отмену», но не совсем так, поскольку разница между двумя состояниями может быть очень большой, и нет четко определенных действий, которые изменяют состояния.

Позвольте мне объяснить на примере: Очень базовое состояние экрана может содержать только одну карту. В состоянии А эта Карта содержит ссылку на «Объект1» с ключом «Ключ1» и «Объект2» с ключом «Ключ2». В состоянии B карта все еще содержит ссылку на «Object1», но «Object2» был изменен, и «Object3» был добавлен. Теперь мне нужно иметь возможность вернуться в состояние A, что потребует «сбрасывания» Object3 и восстановления Object2 в его предыдущем состоянии. Я не могу определить какие-либо пользовательские «действия по отмене», так как я не знаю, какие изменения были внесены в Object2 или даже каков тип Object2. Кроме того, поскольку ссылка для Object2 в состояниях A и B остается неизменной, эти изменения отражаются в состоянии A, поэтому Object2 отличается от того, что было.

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

У кого-нибудь есть другие идеи? Спасибо, Ristretto

Ответы [ 7 ]

13 голосов
/ 03 февраля 2009

Вы пытались заглянуть в Шаблон Memento Design ? Кажется, это особенно хорошо определено для вашей проблемы. Из Википедии:

Шаблон памяти - это программное обеспечение шаблон проектирования, который обеспечивает способность восстановить объект в его предыдущее состояние (отменить через откат).

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

2 голосов
/ 03 февраля 2009

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

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

public List<String> changeTheScreen(List<String> states) {
  return states.cons(states.head() + "x");
}

public void renderTheScreen(String currentState) {
  // TODO: draw the screen given the current state
}

В приведенном выше примере List - это fj.data.List, тип неизменяемого в памяти односвязного списка из библиотеки Functional Java (стандартные библиотеки не имеют неизменяемого списка). Метод будет принимать историю состояний с текущим состоянием в начале списка. Он манипулирует состоянием экрана, создавая новое состояние и помещая его перед новым списком состояний.

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

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

Функциональная Java имеет неизменную карту с именем TreeMap. Вы можете использовать ее следующим образом:

public List<TreeMap<String, Object>>
changeState(List<TreeMap<String, Object>> states) {
  return states.cons(states.head().set("Key1", new Object1("x")));
}
2 голосов
/ 03 февраля 2009

Вы можете проверить Prevayler

0 голосов
/ 03 февраля 2009

В моем проекте мы добились чего-то очень похожего, сериализовав файлы XML. Это хорошо сработало для нас. Все объекты, которые вы хотите вернуть - сериализовать в XML-файл четко определенным образом, чтобы в любое время впоследствии вы могли вернуть состояние из XML-файла.

0 голосов
/ 03 февраля 2009

Если то, что вы хотите, на самом деле является картой, вы можете захотеть взглянуть на постоянные структуры данных; например, постоянные B-деревья.

0 голосов
/ 03 февраля 2009

Вы всегда можете использовать сериализацию;

ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
objectOutputStream.writeObject(object);
objectOutputStream.flush();
byteArrayOutputStream.close();
ByteArrayInputStream istream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
ObjectInputStream objectInputStream= new ObjectInputStream(istream);
Object deserialized = objectInputStream.readObject();
istream.close();

Медленно и неуклюже, но работает.

0 голосов
/ 03 февраля 2009

Мы делаем что-то похожее с сериализацией.

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

Убедитесь, что вы версировали свои объекты и убедитесь, что ваша разность справляется с отсутствующими / новыми полями.

Мы решили сохранить данные в файловой системе, потому что она дает нам (эффективно) неограниченную емкость. Скорость для нас не проблема, но метод файловой системы удивительно быстр, большинство людей не замечают лишних 50-100 мс!

...