设计模式系列—备忘录模式

开发 前端
本篇和大家一起来学习备忘录模式相关内容。

 模式定义

在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,以便以后当需要时能将该对象恢复到原先保存的状态。该模式又叫快照模式。

模版实现如下:

  1. package com.niuh.designpattern.memento.v1; 
  2.  
  3. /** 
  4.  * <p> 
  5.  * 备忘录模式 
  6.  * </p> 
  7.  */ 
  8. public class MementoPattern { 
  9.     public static void main(String[] args) { 
  10.         Originator or = new Originator(); 
  11.         Caretaker cr = new Caretaker(); 
  12.         or.setState("S0"); 
  13.         System.out.println("初始状态:" + or.getState()); 
  14.         cr.setMemento(or.createMemento()); //保存状态       
  15.         or.setState("S1"); 
  16.         System.out.println("新的状态:" + or.getState()); 
  17.         or.restoreMemento(cr.getMemento()); //恢复状态 
  18.         System.out.println("恢复状态:" + or.getState()); 
  19.     } 
  20.  
  21. //备忘录 
  22. class Memento { 
  23.     private String state; 
  24.  
  25.     public Memento(String state) { 
  26.         this.state = state; 
  27.     } 
  28.  
  29.     public void setState(String state) { 
  30.         this.state = state; 
  31.     } 
  32.  
  33.     public String getState() { 
  34.         return state; 
  35.     } 
  36.  
  37. //发起人 
  38. class Originator { 
  39.     private String state; 
  40.  
  41.     public void setState(String state) { 
  42.         this.state = state; 
  43.     } 
  44.  
  45.     public String getState() { 
  46.         return state; 
  47.     } 
  48.  
  49.     public Memento createMemento() { 
  50.         return new Memento(state); 
  51.     } 
  52.  
  53.     public void restoreMemento(Memento m) { 
  54.         this.setState(m.getState()); 
  55.     } 
  56.  
  57. //管理者 
  58. class Caretaker { 
  59.     private Memento memento; 
  60.  
  61.     public void setMemento(Memento m) { 
  62.         memento = m; 
  63.     } 
  64.  
  65.     public Memento getMemento() { 
  66.         return memento; 
  67.     } 

输出结果如下:

  1. 初始状态:S0 
  2. 新的状态:S1 
  3. 恢复状态:S0 

解决的问题

备忘录模式能记录一个对象的内部状态,当用户后悔时能撤销当前操作,使数据恢复到它原先的状态。

每个人都有犯错误的时候,都希望有种“后悔药”能弥补自己的过失,让自己重新开始,但现实是残酷的。在计算机应用中,客户同样会常常犯错误,能否提供“后悔药”给他们呢?当然是可以的,而且是有必要的。这个功能由“备忘录模式”来实现。

模式组成

 

备忘录模式的核心是设计备忘录类以及用于管理备忘录的管理者类。

实例说明

实例概况

 

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

使用步骤

 

步骤1:定义备忘录角色,用于存储角色状态。

  1. 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.     } 

步骤2:定义发起人角色(当前游戏角色),记录当前游戏角色的生命力、攻击力、防御力。通过saveState()方法来保存当前状态,通过recoveryState()方法来恢复角色状态。

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

步骤3:定义管理者角色,角色状态管理者

  1. 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.     } 

步骤4:测试输出

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

输出结果

  1. 角色当前状态: 
  2. 体力:100 
  3. 攻击力:100 
  4. 防御力: 100 
  5. ----------------- 
  6. 角色当前状态: 
  7. 体力:0 
  8. 攻击力:0 
  9. 防御力: 0 
  10. ----------------- 
  11. 角色当前状态: 
  12. 体力:100 
  13. 攻击力:100 
  14. 防御力: 100 

优点

备忘录模式是一种对象行为型模式,其主要优点如下。

  • 提供了一种可以恢复状态的机制。当用户需要时能够比较方便地将数据恢复到某个历史的状态。
  • 实现了内部状态的封装。除了创建它的发起人之外,其他对象都不能够访问这些状态信息。
  • 简化了发起人类。发起人不需要管理和保存其内部状态的各个备份,所有状态信息都保存在备忘录中,并由管理者进行管理,这符合单一职责原则。

缺点

资源消耗大。如果要保存的内部状态信息过多或者特别频繁,将会占用比较大的内存资源。

注意事项

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

应用场景

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

模式的扩展

 

在备忘录模式中,有单状态备份的例子,也有多状态备份的例子。可以结合原型模式混合使用。在备忘录模式中,通过定义“备忘录”来备份“发起人”的信息,而原型模式的 clone() 方法具有自备份功能,所以,如果让发起人实现 Cloneable 接口就有备份自己的功能,这时可以删除备忘录类,其结构如下:

源码中的应用

  1. #Spring 
  2. org.springframework.binding.message.StateManageableMessageContext 

StateManageableMessageContext 部分源码

  1. public interface StateManageableMessageContext extends MessageContext { 
  2.  
  3.    /** 
  4.     * Create a serializable memento, or token representing a snapshot of the internal state of this message context. 
  5.     * @return the messages memento 
  6.     */ 
  7.    public Serializable createMessagesMemento(); 
  8.  
  9.    /** 
  10.     * Set the state of this context from the memento provided. After this call, the messages in this context will match 
  11.     * what is encapsulated inside the memento. Any previous state will be overridden. 
  12.     * @param messagesMemento the messages memento 
  13.     */ 
  14.    public void restoreMessages(Serializable messagesMemento); 
  15.  
  16.    /** 
  17.     * Configure the message source used to resolve messages added to this context. May be set at any time to change how 
  18.     * coded messages are resolved. 
  19.     * @param messageSource the message source 
  20.     * @see MessageContext#addMessage(MessageResolver) 
  21.     */ 
  22.    public void setMessageSource(MessageSource messageSource); 

PS:以上代码提交在 Github :

https://github.com/Niuh-Study/niuh-designpatterns.git

责任编辑:姜华 来源: 今日头条
相关推荐

2023-10-07 00:14:53

2023-10-31 09:07:16

备忘录模式保存

2023-04-19 08:03:52

Go设计模式

2018-12-24 21:40:12

2023-10-10 15:26:30

内存泄露OOM

2014-04-17 10:30:41

Linux 命令黑白备忘录

2017-03-21 11:02:59

基础深度学习备忘录

2011-04-11 10:03:32

钱伯斯思科

2011-08-16 18:38:23

Core Animat动画

2018-06-20 13:14:16

MySQL数据优化查询备忘录

2013-08-29 10:50:48

移动网站性能优化移动web

2016-03-03 10:09:26

2022-01-12 13:33:25

工厂模式设计

2020-10-23 09:40:26

设计模式

2020-11-04 08:54:54

状态模式

2020-11-03 13:05:18

命令模式

2021-03-08 00:12:44

Grid 备忘录 函数

2011-12-07 09:19:49

JavaJ2MEBicaVM

2021-03-02 08:50:31

设计单例模式

2021-09-29 13:53:17

抽象工厂模式
点赞
收藏

51CTO技术栈公众号