备忘录模式(Memento Design Pattern)

19-04-23 jdon
              

目的

在不违反封装的情况下,捕获对象的内部状态并将其外部化,以便稍后将该对象还原到此状态。

现实中最好的例子之一是文本编辑器,我们可以随时保存它的数据,并使用“撤消”将其恢复到以前保存的状态。

也称为

令牌(taken)

结构

参与者

1.Memento 

  • 该组件存储Originator对象的内部状态。纪念品可以根据发起人的判断,尽可能多地存储发起人的内部状态。

  • 此组件可防止发件人以外的对象访问。Mementos有效地有两个接口。看守人看到了Memento的狭窄界面 - 它只能将纪念品传递给其他物品。相比之下,Originator看到了一个宽广的界面,可以让它访问将自身恢复到之前状态所需的所有数据。理想情况下,只允许制作纪念品的发起人进入纪念品的内部状态。

2.Originator(创建者)

  • 该组件创建一个包含其当前内部状态快照的Memento 。

  • 该组件使用memento来恢复其内部状态。

3.Caretaker(看守者)

  • 它负责纪念品的保管。

  • 它从未对纪念品的内容进行操作或检查

源代码

备忘录模式是通过两个对象实现的——创建者(Originator)和看守者(Caretaker)。

Originator是需要保存和恢复其状态的对象,它使用一个内部类来保存对象的状态。内部类称为memento,它是私有的,因此不能从其他对象访问它。

Caretaker是帮助类,负责通过Memento对象存储和恢复Originator的状态。由于Memento对Originator是私有的,因此Caretaker无法访问它,它作为对象存储在Caretaker中。

第1步:让我们创建Originator Class。

public class FileWriterUtil {
 private String fileName;
 private StringBuilder content;
 public FileWriterUtil(String file) {
  this.fileName = file;
  this.content = new StringBuilder();
 }

 @Override
 public String toString() {
  return this.content.toString();
 }
 public void write(String str) {
  content.append(str);
 }
 public Memento save() {
  return new Memento(this.fileName, this.content);
 }
 public void undoToLastSave(Object obj) {
  Memento memento = (Memento) obj;
  this.fileName = memento.fileName;
  this.content = memento.content;
 }
 private class Memento {
  private String fileName;
  private StringBuilder content;
  public Memento(String file, StringBuilder content) {
   this.fileName = file;
   //notice the deep copy so that Memento and FileWriterUtil
   content variables don 't refer to same object
   this.content = new StringBuilder(content);
  }
 }
}

注意Memento内部类以及save和undo方法的实现。现在我们可以继续实现Caretaker类。

第2步:创建Caretaker类

public class FileWriterCaretaker {
 private Object obj;

 public void save(FileWriterUtil fileWriter) {
  this.obj = fileWriter.save();
 }
 
 public void undo(FileWriterUtil fileWriter) {
  fileWriter.undoToLastSave(obj);
 }
 
}

注意, caretaker对象是以对象的形式包含保存的状态,因此它不能更改其数据,也不了解其结构。

第3步:让我们测试memento模式。编写一个简单的测试程序,它将使用我们的memento实现。

public class FileWriterClient {
 public static void main(String args) {
  FileWriterCaretaker caretaker = new FileWriterCaretaker();
  FileWriterUtil fileWriter = new FileWriterUtil("data.txt");
  fileWriter.write("First Set of Data\n");
  System.out.println(fileWriter + "\n\n");
  // lets save the file
  caretaker.save(fileWriter);
  //now write something else
  fileWriter.write("Second Set of Data\n");
  //checking file contents
  System.out.println(fileWriter + "\n\n");
  //lets undo to last save
  caretaker.undo(fileWriter);
 }

适用性

使用Memento模式时

  • 必须保存对象状态的快照,以便以后可以将其恢复到该状态

  • 获取状态的直接接口将暴露实现细节并破坏对象的封装