• 简说设计模式——备忘录模式


    一、什么是备忘录模式

      备忘录这个词汇大家应该都不陌生,我就经常使用备忘录来记录一些比较重要的或者容易遗忘的信息,与之相关的最常见的应用有许多,比如游戏存档,我们玩游戏的时候肯定有存档功能,旨在下一次登录游戏时可以从上次退出的地方继续游戏,或者对复活点进行存档,如果挂掉了则可以读取复活点的存档信息重新开始。与之相类似的就是数据库的事务回滚,或者重做日志redo log等。

      备忘录模式(Memento),在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存着这个状态。这样以后就可将该对象恢复到原先保存的状态。UML结构图如下:

      其中,Originator是发起人,负责创建一个备忘录Memento,用以记录当前时刻它的内部状态,并可使用备忘录恢复内部状态;Memento是备忘录,负责存储Originator对象的内部状态,并可防止Originator以外的其他对象访问备忘录Memento;Caretaker是管理者,负责保存好备忘录的Memento,不能对备忘录的内容进行操作或检查。

      1. 发起人角色

      记录当前时刻的内部状态,并负责创建和恢复备忘录数据,允许访问返回到先前状态所需的所有数据。

     1 public class Originator {
     2     
     3     private String state;
     4 
     5     public String getState() {
     6         return state;
     7     }
     8 
     9     public void setState(String state) {
    10         this.state = state;
    11     }
    12     
    13     public Memento createMento() {
    14         return (new Memento(state));
    15     }
    16     
    17     public void setMemento(Memento memento) {
    18         state = memento.getState();
    19     }
    20     
    21     public void show() {
    22         System.out.println("state = " + state);
    23     }
    24     
    25 }

      2. 备忘录角色

      负责存储Originator发起人对象的内部状态,在需要的时候提供发起人需要的内部状态。

     1 public class Memento {
     2     
     3     private String state;
     4     
     5     public Memento(String state) {
     6         this.state = state;
     7     }
     8     
     9     public String getState() {
    10         return state;
    11     }
    12 
    13 }

      3. 备忘录管理员角色

      对备忘录进行管理、保存和提供备忘录,只能将备忘录传递给其他角色。

     1 public class Caretaker {
     2     
     3     private Memento memento;
     4 
     5     public Memento getMemento() {
     6         return memento;
     7     }
     8 
     9     public void setMemento(Memento memento) {
    10         this.memento = memento;
    11     }
    12     
    13 }

      4. Client客户端

      下面编写一小段代码测试一下,即先将状态置为On,保存后再将状态置为Off,然后通过备忘录管理员角色恢复初始状态。

     1 public class Client {
     2     
     3     public static void main(String[] args) {
     4         Originator originator = new Originator();
     5         originator.setState("On");    //Originator初始状态
     6         originator.show();
     7         
     8         Caretaker caretaker = new Caretaker();
     9         caretaker.setMemento(originator.createMento());
    10         
    11         originator.setState("Off");    //Originator状态变为Off
    12         originator.show();
    13         
    14         originator.setMemento(caretaker.getMemento());    //回复初始状态
    15         originator.show();
    16     }
    17 
    18 }

      运行结果如下:

      

    二、备忘录模式的应用

      1. 何时使用

    • 需要记录一个对象的内部状态时,为了允许用户取消不确定或者错误的操作,能够恢复到原先的状态

      2. 方法

    • 通过一个备忘录类专门存储对象状态

      3. 优点

    • 给用户提供了一种可以恢复状态的机制,可以使用能够比较方便地回到某个历史的状态
    • 实现了信息的封装,使得用户不需要关心状态的保存细节

      4. 缺点

    • 消耗资源

      5. 使用场景

    • 需要保存和恢复数据的相关场景
    • 提供一个可回滚的操作,如ctrl+z、浏览器回退按钮、Backspace键等
    • 需要监控的副本场景

      6. 应用实例

    • 游戏存档
    • ctrl+z键、浏览器回退键等(撤销/还原)
    • 棋盘类游戏的悔棋
    • 数据库事务的回滚

      7. 注意事项

    • 为了符合迪米特法则,需要有一个管理备忘录的类
    • 不要在频繁建立备份的场景中使用备忘录模式。为了节约内存,可使用原型模式+备忘录模式

    三、备忘录模式的实现

      下面以游戏存档为例,看一下如何用备忘录模式实现。UML图如下:

      1. 游戏角色

      简单记录了游戏角色的生命力、攻击力、防御力,通过saveState()方法来保存当前状态,通过recoveryState()方法来恢复角色状态。

     1 public class GameRole {
     2     
     3     private int vit;    //生命力
     4     private int atk;    //攻击力
     5     private int def;    //防御力
     6     
     7     public int getVit() {
     8         return vit;
     9     }
    10     public void setVit(int vit) {
    11         this.vit = vit;
    12     }
    13     public int getAtk() {
    14         return atk;
    15     }
    16     public void setAtk(int atk) {
    17         this.atk = atk;
    18     }
    19     public int getDef() {
    20         return def;
    21     }
    22     public void setDef(int def) {
    23         this.def = def;
    24     }
    25     
    26     //状态显示
    27     public void stateDisplay() {
    28         System.out.println("角色当前状态:");
    29         System.out.println("体力:" + this.vit);
    30         System.out.println("攻击力:" + this.atk);
    31         System.out.println("防御力: " + this.def);
    32         System.out.println("-----------------");
    33     }
    34     
    35     //获得初始状态
    36     public void getInitState() {
    37         this.vit = 100;
    38         this.atk = 100;
    39         this.def = 100;
    40     }
    41     
    42     //战斗后
    43     public void fight() {
    44         this.vit = 0;
    45         this.atk = 0;
    46         this.def = 0;
    47     }
    48     
    49     //保存角色状态
    50     public RoleStateMemento saveState() {
    51         return (new RoleStateMemento(vit, atk, def));
    52     }
    53     
    54     //恢复角色状态
    55     public void recoveryState(RoleStateMemento memento) {
    56         this.vit = memento.getVit();
    57         this.atk = memento.getAtk();
    58         this.def = memento.getDef();
    59     }
    60 
    61 }

      2. 角色状态存储箱

      备忘录类,用于存储角色状态。

     1 public class RoleStateMemento {
     2     
     3     private int vit;    //生命力
     4     private int atk;    //攻击力
     5     private int def;    //防御力
     6     
     7     public RoleStateMemento(int vit, int atk, int def) {
     8         this.vit = vit;
     9         this.atk = atk;
    10         this.def = def;
    11     }
    12 
    13     public int getVit() {
    14         return vit;
    15     }
    16 
    17     public void setVit(int vit) {
    18         this.vit = vit;
    19     }
    20 
    21     public int getAtk() {
    22         return atk;
    23     }
    24 
    25     public void setAtk(int atk) {
    26         this.atk = atk;
    27     }
    28 
    29     public int getDef() {
    30         return def;
    31     }
    32 
    33     public void setDef(int def) {
    34         this.def = def;
    35     }
    36     
    37 }

      3. 角色状态管理者

      备忘录管理者。

     1 public class RoleStateCaretaker {
     2     
     3     private RoleStateMemento memento;
     4 
     5     public RoleStateMemento getMemento() {
     6         return memento;
     7     }
     8 
     9     public void setMemento(RoleStateMemento memento) {
    10         this.memento = memento;
    11     }
    12 
    13 }

      4. Client客户端

      下面编写一个简单的程序测试一下,编写逻辑大致为打boss前存档,打boss失败了,读档。

     1 public class Client {
     2     
     3     public static void main(String[] args) {
     4         //打boss前
     5         GameRole gameRole = new GameRole();
     6         gameRole.getInitState();
     7         gameRole.stateDisplay();
     8         
     9         //保存进度
    10         RoleStateCaretaker caretaker = new RoleStateCaretaker();
    11         caretaker.setMemento(gameRole.saveState());
    12         
    13         //打boss失败
    14         gameRole.fight();
    15         gameRole.stateDisplay();
    16         
    17         //恢复状态
    18         gameRole.recoveryState(caretaker.getMemento());
    19         gameRole.stateDisplay();
    20     }
    21 
    22 }

      运行结果如下:

      

      源码地址:https://gitee.com/adamjiangwh/GoF  

  • 相关阅读:
    第一次讲课
    请允许我悄悄的爱你一次好吗 zz
    装.NET实在是一种折磨,
    第一次听课
    路,在何方?
    UNION和UNION ALL 的区别
    利用jquery操作select下拉列表框
    理解 collate Chinese_PRC_CI_AS
    sql 中 case when 实例
    sql 中 case when 实例(2)
  • 原文地址:https://www.cnblogs.com/adamjwh/p/11018268.html
Copyright © 2020-2023  润新知