在Java中实现Memento模式的不同方法

我正在对Memento模式进行一些研究,似乎我遇到的大多数例子看起来都相对相似(将一个字符串保存到数组中并在需要时恢复它)现在纠正我,如果我错了但我相信我刚才描述的方法是“对象克隆”,但是实现Memento模式的其他方法是什么?

从我在序列化上所采用的内容可以使用,但似乎有一个灰色区域,人们说它违反了对象的封装,并且由于这个而不是实现Memento Pattern的一种方式。

那么,是否有人能够阐明实施该模式的方法? 我的研究提出了各种不同的东西混合物,让一切变得混乱。

谢谢

Java Collections框架定义了Queue ,它可以提供帮助。

候选代码:

 public final class Memento { // List of saved values private final Queue queue = new ArrayDeque(); // Last entered value, whether it has been saved or not private T currentValue; // No initial state, ie currentValue will be null on construction, hence // no constructor // Set a value, don't save it public void set(final T value) { currentValue = value; } // Persist the currently saved value public void persist() { queue.add(currentValue); } // Return the last saved value public T lastSaved() { return queue.element(); } // Return the last entered value public T lastEntered() { return currentValue; } } 

值得注意的是,此代码中缺少许多内容,但很容易实现:

  • 恢复到上次保存的值;
  • 没有检查空值;
  • T没有实现Serializable ;
  • 便利方法(比如,添加一个值并使其成为最后保存的状态);
  • 代码不是线程安全的

等等。

示例代码:

 public static void main(final String... args) { final Memento memento = new Memento(); memento.set("state1"); System.out.println(memento.lastEntered()); // "state1" memento.persist(); memento.set("state2"); System.out.println(memento.lastEntered()); // "state2" System.out.println(memento.lastSaved()); // "state1" } 

实际上:这是一个可以改进的脑卒中实现,但可以作为基础 – 扩展它取决于您的需要;)

memento实现可能带来的一个常见问题是,通常需要许多表示不同类型对象的内部状态的类。 或者memento实现必须将对象状态序列化为其他forms(例如,序列化的java对象)。

这是一个memento实现的草图,它不依赖于每个类的特定memento类,其状态将被捕获以进行撤消/重做支持。

首先要介绍一个基本概念:

 public interface Reference { T get(); void set(T value); } 

这是java.lang.ref.Reference的抽象,因为该类用于垃圾收集目的。 但我们需要将它用于业务逻辑。 基本上,引用封装了一个字段。 所以他们打算像这样使用:

 public class Person { private final Reference lastName; private final Reference dateOfBirth; // constructor ... public String getLastName() { return lastName.get(); } public void setLastName(String lastName) { this.lastName.set(lastName); } public Date getDateOfBirt() { return dateOfBirth.get(); } public void setDateOfBirth(Date dateOfBirth) { this.dateOfBirth.set(dateOfBirth); } } 

请注意,使用这些引用进行对象实例化可能不是那么简单,但我们将其留在此处。

现在,这里是纪念品实施的细节:

 public interface Caretaker { void addChange(Change change); void undo(); void redo(); void checkpoint(); } public interface Change { Change createReversal(); void revert(); } 

基本上, Change代表可识别对象状态的单个可识别变更。 通过调用revert方法可以恢复更改,并且可以通过还原createReversal方法创建的Change来恢复该更改的反转。 Caretaker通过addChange方法addChange对象状态的更改。 通过调用undoredo方法, Caretaker恢复或redo (即恢复更改的反转)所有更改,直到到达下一个检查点。 检查点表示所有观察到的更改将累积到一个点,该更改将所有已更改对象的所有状态从一个有效配置转换为另一个有效配置。 检查点通常在操作之前或之后创建。 这些是通过checkpoint方法创建的。

现在,这里是如何使用Caretaker Reference

 public class ReferenceChange implements Change { private final Reference reference; private final T oldValue; private final T currentReferenceValue; public ReferenceChange(Reference reference, T oldValue, T currentReferenceValue) { super(); this.reference = reference; this.oldValue = oldValue; this.currentReferenceValue = currentReferenceValue; } @Override public void revert() { reference.set(oldValue); } @Override public Change createReversal() { return new ReferenceChange(reference, currentReferenceValue, oldValue); } } public class CaretakingReference implements Reference { private final Reference delegate; private final Caretaker caretaker; public CaretakingReference(Reference delegate, Caretaker caretaker) { super(); this.delegate = delegate; this.caretaker = caretaker; } @Override public T get() { return delegate.get(); } @Override public void set(T value) { T oldValue = delegate.get(); delegate.set(value); caretaker.addChange(new ReferenceChange(delegate, oldValue, value)); } } 

存在一个表示Reference的值如何Change 。 设置CaretakingReference时会创建此Change 。 在此实现中,需要CaretakingReference实现中的嵌套Reference ,因为ReferenceChangerevert不应通过CaretakingReference触发新的addChange

集合属性不需要使用Reference 。 在这种情况下,应该使用触发看护的自定义实现。 基元可以与自动装箱一起使用。

此实现通过始终直接使用引用而不是字段来推断额外的运行时和内存成本。