博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
备忘录模式
阅读量:6830 次
发布时间:2019-06-26

本文共 5593 字,大约阅读时间需要 18 分钟。

备忘录模式

标签 : Java与设计模式


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

(图片来源: )
将保存细节封装在Memento中, 后面即使改动了保存细节也不会影响客户端.


模式实现

案例: 游戏进度保存在攻击Boss前, 将当前游戏进度保存, 万一失败还可从保存点又一次開始.


Originator-原发器

  • 负责创建一个备忘录Memento, 用以记录当前时刻它的内部状态(决定Memento存储哪些内部状态 -不一定是全部属性);
  • 可使用备忘录恢复内部状态.
/** * 游戏角色, 原发器 * * @author jifang * @since 16/8/29 上午10:05. */public class GameRoleOriginator {
private int vit; // 生命值 private int atk; // 攻击力 private int def; // 防御力 public GameRoleOriginator(int vit, int atk, int def) { this.vit = vit; this.atk = atk; this.def = def; } public void fight() { vit -= 10; atk -= 8; def += 10; } public RoleStateMemento save() { return new RoleStateMemento(vit, atk, def); } public void recover(RoleStateMemento memento) { this.setVit(memento.getVit()); this.setAtk(memento.getAtk()); this.setDef(memento.getDef()); } 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; } @Override public String toString() { return "GameRoleOriginator{" + "vit=" + vit + ", atk=" + atk + ", def=" + def + '}'; }}

Memento-备忘录

  • 负责存储Originator的内部状态(与Originator共同决定存储原发器哪些内部状态);
  • 防止Originator以外对象訪问备忘录. Memento实际应该有两个接口: Caretaker仅仅能看到一个窄接口 -仅仅能将备忘录传递给其它对象. Originator能够看到一个宽接口, 同意它訪问返回先前状态所需的全部数据(C++中可由friend提供支持). 理想的情况是仅仅同意生成本备忘录的那个原发器訪问本备忘录的内部状态.
public class RoleStateMemento {    private int vit;    private int atk;    private int def;    public RoleStateMemento(int vit, int atk, int def) {        this.vit = vit;        this.atk = atk;        this.def = 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;    }}

Caretaker-负责人

  • 负责保存好备忘录Memento;
  • 不能对备忘录内容进行操作或检查.
public class RoleStateCaretaker {    private Deque
stack = new LinkedList<>(); public void save(RoleStateMemento memento) { stack.push(memento); } public RoleStateMemento checkout() { return stack.pop(); }}
  • Client
public class Client {    @Test    public void client() {        RoleStateCaretaker caretaker = new RoleStateCaretaker();        GameRoleOriginator originator = new GameRoleOriginator(100, 50, 50);        System.out.println("角色初始状态: " + originator);        // 保存进度        caretaker.save(originator.save());        System.out.println("fight boss...");        originator.fight();        System.out.println("阻击Boss后的状态: " + originator);        originator.recover(caretaker.checkout());        System.out.println("恢复后的状态: " + originator);    }}

序列化全部属性

假设Memento须要保存的是Originator的全部属性, 那么可将Originator的全部属性都存储到一个Map<String, Object>结构中由Caretaker保存, 这样就节省了Memento中间类的开发成本. 甚至还可将Originator序列化为二进制流/字符串存储到持久化设备中(如磁盘、DB、Redis), 节省内存开销, 以下演示将Originator转化为Map<String, Object>存储:

  • Originator
    注意save()/recover()的变化:
public class GameRoleOriginator {    private int vit;    // 生命值    private int atk;    // 攻击力    private int def;    // 防御力    // ...    public void fight() {        vit -= 10;        atk -= 8;        def += 10;    }    public Map
save() { try { return BeanUtil.bean2Map(this); } catch (IllegalAccessException e) { throw new RuntimeException(e); } } public void recover(Map
memento) { GameRoleOriginator bean; try { bean = BeanUtil.map2Bean(memento); } catch (IllegalAccessException | InstantiationException | NoSuchFieldException e) { throw new RuntimeException(e); } this.setVit(bean.getVit()); this.setAtk(bean.getAtk()); this.setDef(bean.getDef()); } // ...}
  • BeanUtil
public class BeanUtil {    public static Map
bean2Map(Object object) throws IllegalAccessException { Map
map = new HashMap<>(); Class
clazz = object.getClass(); map.put("class", clazz); Field[] fields = clazz.getDeclaredFields(); for (Field field : fields) { field.setAccessible(true); String key = field.getName(); Object value = field.get(object); map.put(key, value); } return map; } public static
T map2Bean(Map
map) throws IllegalAccessException, InstantiationException, NoSuchFieldException { Class
clazz = (Class
) map.get("class"); Field[] fields = clazz.getDeclaredFields(); Object object = clazz.newInstance(); for (Field field : fields) { field.setAccessible(true); Object value = map.get(field.getName()); field.set(object, value); } return (T) object; }}

小结

  • 适用性

    • 必须保存一个对象在某一个时刻的(部分)状态, 这样以后须要时它才干恢复到先前的状态;
    • 假设一个用接口来让其它对象直接得到这些状态, 将会暴露对象的实现细节并破坏对象的封装性, 而Memento能够把复杂的对象内部信息对其它的对象屏蔽起来, 从而能够恰当的保持封装的边界.
      • 事务回滚;
      • 棋类游戏中的悔棋;
      • PhotoShop的历史记录.
  • 相关模式

    • : 假设在使用命令模式时须要实现命令的撤销, 那么可用Memento来存储可撤销的状态.
    • : 备忘录可用于迭代.

參考:

  • by 攻城师@翡青
    • Email: feiqing.zjf@gmail.com
    • 博客: -
    • 微博: -

转载地址:http://bqjkl.baihongyu.com/

你可能感兴趣的文章
C++类构造函数初始化列表(转)
查看>>
13最佳WordPress的维护插件
查看>>
Missing Screenshot 的解决方案
查看>>
jQuery:1.5.4.3,表格变色(单击行,把当行的单选按钮(radio)设为选中状态,并应用当前样式)...
查看>>
oracle11gR2安装示例数据库
查看>>
解决ssh的"Write failed: Broken pipe"问题
查看>>
Java 网络编程(五) 使用TCP/IP的套接字(Socket)进行通信
查看>>
拒绝alert调试js,浏览器调试js大全(火狐firefox浏览器,谷歌chrome 浏览器,微软ie9浏览器等)...
查看>>
《深入理解Nginx》阅读与实践(三):使用upstream和subrequest访问第三方服务
查看>>
NGUI:HUD Text(头顶伤害漂浮文字)
查看>>
HTML/CSS/Javascript代码在线压缩、格式化(美化)工具
查看>>
linux命令学习-复制(cp,scp)
查看>>
cocos2d-x开发记录:二,基本概念(粒子系统,Scheduler和定时器)
查看>>
去掉Flex4生成的SWF加载时的进度条
查看>>
如何使用 MasterPage
查看>>
load dll
查看>>
Linux给指定用户或全部用户(已登录)发送消息
查看>>
C语言 队列 链式结构 实现
查看>>
关于同一用户不能同时登录问题的探讨(1/2)
查看>>
android-support-v7-appcompat的配置使用
查看>>