Java设计模式--备忘录模式

备忘录模式(别名:标记)

在不破坏封装性的前提下,捕捉一个对象的内部状态,并在该对象之外保存这个状态,这样以后就可将该对象恢复到原先保存的状态。

Memento Pattern(Another Name: Token)

Without violating encapsulation, capture and externalize an object original state so that the object can be restored to this state later.

类图

模式的结构与使用

备忘录模式的结构中包括三种角色。

  • 原发者(Originator):需要在某个时刻保存其状态的对象。原发者负责创建备忘录,比如使用createMemento()方法创建一个备忘录,然后原发者使用该备忘录记录自己的状态。当原发者需要恢复某个时刻的状态时,它通过获得相应备忘录中的数据来恢复那个时刻的状态,比如原发者调用restoreFromMemento(Memento menm)方法,并通过参数mem指定的备忘录恢复状态。
  • 备忘录(Memento):负责存储原发者状态的对象,创建备忘录的类和创建原发者的类在同一个包中,该类提供的访问数据的方法都是友好方法,使得只有和原发者在同一个包中的类的实例才可以访问备忘录中的数据。
  • 负责人(Caretaker):负责管理保存备忘录的对象。负责人如果不和原发者在同一个包中,就不能对备忘录中的内容进行修改或读取。如果需要将备忘录保存到磁盘,负责人可以使用对象流将备忘录写入文件。

简单的例子

Memento的备忘录类Memento.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package Memento;
import java.io.Serializable;
public class Memento implements Serializable {
private long state;
void setPositionState(long state) {
this.state = state;
}
long getPosition() {
return state;
}
}

一个小的工具类ReadPhrase.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
ppackage Memento;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
public class ReadPhrase {
long readPosition;
File file;
RandomAccessFile in;
String phrase = null;
public ReadPhrase(File file) {
this.file = file;
try {
in = new RandomAccessFile(file, "r");
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
public Memento createMemento() {
Memento mem = new Memento();
System.out.println("----" + readPosition);
mem.setPositionState(readPosition);
return mem;
}
public void restoreFromMemento(Memento mem) {
readPosition = mem.getPosition();
}
public String readLine() {
try {
in.seek(readPosition);
phrase = in.readLine();
if (phrase != null) {
byte b[] = phrase.getBytes("UTF-8");
phrase = new String(b);
}
readPosition = in.getFilePointer();
System.out.println("readPosition:" + readPosition);
} catch (IOException e) {
e.printStackTrace();
}
return phrase;
}
public void closeRead() {
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}

负责人的类Caretaker.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
package Memento;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
public class Caretaker {
File file;
private Memento memento = null;
public Caretaker() {
file = new File("saveObject.txt");
}
public Memento getMemento() {
if (file.exists()) {
try {
FileInputStream in = new FileInputStream("saveObject.txt");
ObjectInputStream inObject = new ObjectInputStream(in);
memento = (Memento) inObject.readObject();
} catch (Exception e) {
e.printStackTrace();
}
}
return memento;
}
public void saveMemento(Memento memento) {
FileOutputStream out;
try {
out = new FileOutputStream("D:/09soft/MyEclipse 10/yanning/saveObject.txt");
ObjectOutputStream outObject = new ObjectOutputStream(out);
outObject.writeObject(memento);
} catch (Exception e) {
e.printStackTrace();
}
}
}

测试类Application.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
package Memento;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.Scanner;
public class Application {
public static void main(String[] args) {
Scanner reader = new Scanner(System.in);
ReadPhrase readPhrase = new ReadPhrase(new File("D:/09soft/MyEclipse 10/yanning/src/Memento/phrase.txt"));
File favorPhrase = new File("favorPhrase.txt");
RandomAccessFile out = null;
try {
out = new RandomAccessFile(favorPhrase, "rw");
} catch (FileNotFoundException e) {
e.printStackTrace();
}
System.out.println("是否从上次读取的位置继续读取成语(输入y或n)");
String answer = reader.nextLine();
if (answer.startsWith("y")||answer.startsWith("Y")) {
Caretaker caretaker = new Caretaker(); //创建负责人
Memento memento = caretaker.getMemento(); //得到备忘录
if (memento != null) {
readPhrase.restoreFromMemento(memento); //使用备忘录恢复状态
}
String phrase = null;
while((phrase = readPhrase.readLine()) != null) {
System.out.println(phrase);
System.out.println("是否将该成语保存到" + favorPhrase.getName());
answer = reader.nextLine();
if (answer.startsWith("y")||answer.startsWith("Y")) {
try {
out.seek(favorPhrase.length());
byte[] b = phrase.getBytes();
out.write(b);
out.write(' ');
} catch (IOException e) {
e.printStackTrace();
}
}
System.out.println("是否继续读取成语?(输入y或n)");
answer = reader.nextLine();
if (answer.startsWith("y")||answer.startsWith("Y")) {
continue;
} else {
readPhrase.closeRead();
caretaker.saveMemento(readPhrase.createMemento());
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
System.exit(0);
}
}
System.out.println("读完全部成语");
}
System.exit(0);
}
}

备忘录模式的优点

  • 备忘录模式使用备忘录可以把原发者的内部状态保存起来,使只有很“亲密的”的对象可以访问备忘录中的数据。
  • 备忘录模式强调了类设计单一责任原则,即将状态的刻画和保存分开。

适用备忘录模式的情景

下列情况之一就可以考虑使用备忘录模式

  • 必须保存一个对象在某一时刻的全部或部分状态,以便在需要时恢复该对象先前的状态。
  • 一个对象不想通过提供public权限的,诸如getXXX()的方法让其他对象得到自己的内部状态。

注:如果备忘录需要存储大量的数据或非常频繁地创建备忘录,可能会导致非常大的存储开销。

下载源码请到

MyGitHub

文章目录
  1. 1. 备忘录模式(别名:标记)
  2. 2. Memento Pattern(Another Name: Token)
  3. 3. 类图
  4. 4. 模式的结构与使用
  5. 5. 简单的例子
    1. 5.1. Memento的备忘录类Memento.java
    2. 5.2. 一个小的工具类ReadPhrase.java
    3. 5.3. 负责人的类Caretaker.java
    4. 5.4. 测试类Application.java
  6. 6. 备忘录模式的优点
  7. 7. 适用备忘录模式的情景
    1. 7.1. 下列情况之一就可以考虑使用备忘录模式
  8. 8. 下载源码请到