备忘录模式

PPG007 ... 2021-12-30 About 3 min

# 备忘录模式

# 定义

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

# 角色

  • Originator 发起人角色:记录当前时刻的内部状态,负责定义哪些属于备份范围的状态,负责创建和恢复备忘录数据。
  • Memento 备忘录角色:存储发起人对象的内部状态,在需要的时候提供发起人需要的内部状态。
  • Caretaker 备忘录管理员角色:对备忘录进行管理、保存和提供。

# 示例

发起人角色:

public class GameRole {
    /**
     * vit 生命值
     * atk 攻击力
     * def 防御力
     */
    private int vit;
    private int atk;
    private int def;

    public int getVit() {
        return vit;
    }

    public void setVit(int vit) {
        this.vit = vit;
    }

    public int getAtk() {
        return atk;
    }

    public void setAtk(int atk) {
        this.atk = atk;
    }

    public int getDef() {
        return def;
    }

    public void setDef(int def) {
        this.def = def;
    }

    public void display(){
        System.out.println("====now state====");
        System.out.println("生命值==>"+this.vit);
        System.out.println("攻击力==>"+this.atk);
        System.out.println("防御力==>"+this.def);
    }

    public void getInitState(){
        this.vit=100;
        this.atk=100;
        this.def=100;
    }

    public void fight(){
        this.vit=0;
        this.atk=0;
        this.def=0;
    }

    public RoleStateMemento saveState(){
        return new RoleStateMemento(this.vit, this.atk, this.def);
    }

    public void recoveryState(RoleStateMemento stateMemento){
        this.vit=stateMemento.getVit();
        this.atk=stateMemento.getAtk();
        this.def=stateMemento.getDef();
    }
}
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

发起者具有三个属性,且能够保存状态、恢复状态。

备忘录角色:

public class RoleStateMemento {
    /**
     * vit 生命值
     * atk 攻击力
     * def 防御力
     */
    private int vit;
    private int atk;
    private int def;

    public int getVit() {
        return vit;
    }

    public void setVit(int vit) {
        this.vit = vit;
    }

    public int getAtk() {
        return atk;
    }

    public void setAtk(int atk) {
        this.atk = atk;
    }

    public int getDef() {
        return def;
    }

    public void setDef(int def) {
        this.def = def;
    }

    public RoleStateMemento(int vit, int atk, int def) {
        this.vit = vit;
        this.atk = atk;
        this.def = def;
    }
}
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

备忘录管理员角色:

public class RoleStateCaretaker {
    private RoleStateMemento memento;

    public RoleStateMemento getMemento() {
        return memento;
    }

    public void setMemento(RoleStateMemento memento) {
        this.memento = memento;
    }
}
1
2
3
4
5
6
7
8
9
10
11

启动类:

public class Client {
    public static void main(String[] args) {
        GameRole gameRole = new GameRole();
        gameRole.getInitState();
        gameRole.display();

        RoleStateCaretaker roleStateCaretaker = new RoleStateCaretaker();
        roleStateCaretaker.setMemento(gameRole.saveState());

        gameRole.fight();
        gameRole.display();

        gameRole.recoveryState(roleStateCaretaker.getMemento());
        gameRole.display();
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# 备忘录模式的使用场景

  • 需要保存和恢复数据的相关状态场景。
  • 提供一个可回滚的操作。
  • 需要监控的副本场景中:备份一个主线程中的对象。
  • 数据库连接的事务管理。

# 备忘录模式的注意事项

  • 备忘录的生命期:

    建立就要使用,不使用就立刻删除。

  • 备忘录的性能:

    不要在频繁建立备份的场景中使用备忘录模式(比如循环),原因如下:

    • 控制不了备忘录建立的对象数量。
    • 大对象的建立消耗资源。

# 备忘录模式的扩展

# clone 方式的备忘录

发起人实现 Cloneable 接口并重写 clone 方法即可,且不再需要备忘录角色。

发起人:

public class GameRole implements Cloneable{
    /**
     * vit 生命值
     * atk 攻击力
     * def 防御力
     */
    private int vit;
    private int atk;
    private int def;

    public int getVit() {
        return vit;
    }

    public void setVit(int vit) {
        this.vit = vit;
    }

    public int getAtk() {
        return atk;
    }

    public void setAtk(int atk) {
        this.atk = atk;
    }

    public int getDef() {
        return def;
    }

    public void setDef(int def) {
        this.def = def;
    }

    public void display(){
        System.out.println("====now state====");
        System.out.println("生命值==>"+this.vit);
        System.out.println("攻击力==>"+this.atk);
        System.out.println("防御力==>"+this.def);
    }

    public void getInitState(){
        this.vit=100;
        this.atk=100;
        this.def=100;
    }

    public void fight(){
        this.vit=0;
        this.atk=0;
        this.def=0;
    }

    public RoleStateCaretaker saveState(){
        return new RoleStateCaretaker(this.clone());
    }

    public void recoveryState(RoleStateCaretaker caretaker){
        GameRole gameRole = caretaker.getGameRole();
        this.atk=gameRole.getAtk();
        this.def=gameRole.getDef();
        this.vit=gameRole.getVit();
    }

    @Override
    public GameRole clone() {
        try {
            return (GameRole) super.clone();
        } catch (CloneNotSupportedException e) {
            throw new AssertionError();
        }
    }
}
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
64
65
66
67
68
69
70
71
72
73

备忘录管理员:

public class RoleStateCaretaker {

    private GameRole gameRole;

    public GameRole getGameRole() {
        return gameRole;
    }

    public void setGameRole(GameRole gameRole) {
        this.gameRole = gameRole;
    }

    public RoleStateCaretaker(GameRole gameRole) {
        this.gameRole = gameRole;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

启动类:

public class Client {

    public static void main(String[] args) {

        GameRole gameRole = new GameRole();
        gameRole.getInitState();

        RoleStateCaretaker roleStateCaretaker = gameRole.saveState();

        gameRole.display();
        gameRole.fight();
        gameRole.display();

        RoleStateCaretaker roleStateCaretaker1 = gameRole.saveState();

        gameRole.recoveryState(roleStateCaretaker);
        gameRole.display();

        gameRole.recoveryState(roleStateCaretaker1);
        gameRole.display();

    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

# 多状态的备忘录

例如上面的例子中,发起人具有三个属性组成的状态,可以使用 HashMap 键值对的形式进行存储或者采用数据库等方案。

# 多备份的备忘录

即在不同的时间点对状态进行备份,可以将备忘录管理者中的状态改为 HashMap 类型进行存储,或者使用数据技术。

Last update: December 30, 2021 09:14
Contributors: PPG007