用Java创建状态历史

时间:2009-02-03 07:08:14

标签: java history state

我需要在Java应用程序中的几个操作中保存状态历史记录,稍后我可以重新加载以便在某个操作中恢复状态。换句话说,我有一个屏幕,它有一个与之关联的状态,我需要存储它以及历史记录中的任何更改,以便我可以随时恢复屏幕状态。这有点像'撤消',但不完全是因为两个状态之间的差异可能非常大,并且没有明确定义的动作可以改变状态。

让我用一个例子来解释一下: 一个非常基本的屏幕状态可能只包含一个Map。在状态A中,该Map包含对“Object1”的引用,其中键“Key1”和“Object2”具有键“Key2”。在状态B中,Map仍包含对“Object1”的引用,但“Object2”已被修改并且已添加“Object3”。我现在需要能够返回到状态A,这将涉及“丢弃”Object3并将Object2恢复到其先前的状态。我无法定义任何自定义的“撤消操作”,因为我不知道对Object2进行了哪些更改,甚至不知道Object2的类型是什么。此外,因为状态A和B中的Object2的引用保持不变,所以这些更改反映在状态A中,因此Object2与它不同。

我意识到最好的解决方案是实现克隆方法,但是因为我需要支持所有类型的对象(包括基元和标准集合),所以这是不可行的。我想过使用serializable,我会在状态转换发生后立即序列化Map,然后在需要时再将其反序列化,但这似乎是一个非常难看的解决方案。

有没有人有其他想法? 谢谢, 短萃取浓缩咖啡

7 个答案:

答案 0 :(得分:13)

您是否尝试过调查Memento Design Pattern?它似乎特别适合您的问题。来自维基百科:

  

memento模式是一个软件   提供的设计模式   将对象恢复到其中的能力   先前的状态(通过回滚撤消)。

同一页面上还有一个Java implementation的部分,因为你提到它是用Java编写的。

答案 1 :(得分:2)

您可能需要查看Prevayler

答案 2 :(得分:2)

考虑改变观点。而不是改变构成屏幕状态的对象,而是使用不可变状态。这可能听起来像是矛盾,但事实并非如此。

例如,为了简单起见,说你的州由一个字符串组成。显然,由于字符串是不可变的,因此您不必克隆String以保存和修改状态。例如:

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
}

在上面的示例中,Listfj.data.List,来自Functional Java库的不可变内存中单链接列表类型(标准库没有不可变列表) 。该方法将采用状态的历史,当前状态位于列表的前面。它通过创建一个新状态并将其置于新状态列表的前面来操纵屏幕状态。

将同样的原则应用于您想要用作状态的任何类型。确保您的状态完全由 immutable 对象组成(字符串和基元已经是不可变的)。对状态使用不可变对象将为您节省大量的维护难题,并节省内存,因为可以重用不可变的东西而无需克隆。

不可变对象将在其构造函数中初始化,其所有内部字段将为final

Functional Java has an immutable map called TreeMap.您可以通过以下方式使用它:

public List<TreeMap<String, Object>>
changeState(List<TreeMap<String, Object>> states) {
  return states.cons(states.head().set("Key1", new Object1("x")));
}

答案 3 :(得分:0)

我们通过序列化做了类似的事情。

我们将存档数据以序列化形式存储到文件系统。我们需要恢复的对象图的一部分是序列化的,也是主要的对象。

确保您对对象进行版本设置,并确保差异可以处理缺失/新字段。

我们选择存储到文件系统,因为它为我们(有效)提供了无限的容量。速度对我们来说不是问题,但文件系统方法非常快,大多数人都没有注意到额外的50-100ms!

答案 4 :(得分:0)

您可以随时使用序列化;

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();

缓慢而笨重,但有效。

答案 5 :(得分:0)

如果您想要的实际上是地图,您可能需要查看持久数据结构;例如,持久的B树。

答案 6 :(得分:0)

在我的项目中,我们通过序列化为XML文件实现了非常相似的功能。这对我们很有用。您想要获取的所有对象 - 以明确定义的方式在XML文件中序列化,以便随后可以从XML文件中恢复状态。