设计模式之备忘录模式

来源:互联网 发布:制作淘宝优惠券网站 编辑:程序博客网 时间:2024/06/08 05:07

如有转载,请申明:

转载至 http://blog.csdn.net/qq_35064774/article/details/52068311


1 什么是备忘录模式

 

备忘录模式又叫做快照模式或Token模式,是对象的行为模式。

备忘录对象是一个用来存储另一个对象内部状态的快照的对象。备忘录模式的用意是在不破坏封装的条件下,将一个对象的状态捕捉住,并外部化,存储起来,从而可以在将来合适的时候把这个对象还原到存储起来的状态。

 

2 如何实现备忘录模式

同样为了容易理解,我们以一个简单问题为入口。

 

 * 假设你正在开发一款游戏

 * 游戏中有一种道具,使用后,可以记录当前角色的状态,玩家可以在任意时刻恢复这个状态一次

 

首先我们先分析一下需要哪些类。可以确定的是一个Hero类,一个备忘录类以及负责保存备忘录的类。

 

为了让不了解备忘录的同学也容易看清全局,我们先写测试类,熟悉一下这个备忘录怎么个用法。

 

package com.ittianyu.memento;public abstract class Test {public static void main(String[] args) {Hero hero = new Hero(new HeroInfo());System.out.println(hero.getInfo());Caretaker caretaker = new Caretaker();caretaker.saveMemento(hero.createMemento());hero.setInfo(new HeroInfo("dead", 0));System.out.println(hero.getInfo());hero.restoreMemento(caretaker.retrieveMemento());System.out.println(hero.getInfo());}}

 

分析一下这里的代码,首先我们是创建一个Hero类,传入了一个状态。

然后我们创建了保存备忘录的类,暂且称它为管家类,这个管家可以保存hero创建的备忘录。

然后我们改变hero的状态,之后再调用还原的方法。

 

这个过程很简单,但其中存在一些需要注意的问题,不知道你发现没有。

没错,caretaker.retrieveMemento()可以轻易的获取到备忘录,你有没有想过这个备忘录里面的内容可以被轻松的读取和篡改呢。作为备忘录,应该做到只能被发起者(这里是Hero)访问内容。

当然我们的设计者早就考虑到这个问题,在c++中,我们可以把备忘录的方法设为私有的,然后把发起者设为友元,这样这些访问属性的方法只对发起者开放。然而在java中,这样是行不通的,java没有这样的语法。那么还有其他方法保证只有发起者可以访问备忘录的方法吗?

答案是有的,备忘录作为发起者的内部类。但这样就导致另外一个问题,外部的管家类怎么得到这么一个类型呢,内部类只有发起者才能访问。

这个时候我们不得不有技巧性的引入一个标志接口,也就是让这个内部类实现这个接口,那么这个内部类的对象就可以看作是这个接口的实现对象了。在管家类中,我们只需要用接口引用这个对象就行了。真是皆大欢喜。

 

花了这么多篇幅介绍这么一个问题,其实就是java的双重接口问题。以后如果碰到类似问题时,就可以考虑这种实现方式。

 

我们接下来写代码,首先应该是把Hero类定义出来。

 

package com.ittianyu.memento;public class Hero {protected class Memento implements MementoIF {private HeroInfo state;private HeroInfo getState() {return state;}private void setState(HeroInfo state) {this.state = state;}private Memento(HeroInfo state) {super();this.state = state;}}private HeroInfo info; public HeroInfo getInfo() {return info;}public void setInfo(HeroInfo info) {this.info = info;}public Hero(HeroInfo heroInfo) {this.setInfo(heroInfo);}public MementoIF createMemento() {return new Memento(info);}public void restoreMemento(MementoIF retrieveMemento) {HeroInfo info = ((Memento)retrieveMemento).getState();this.setInfo(info);}}


 

上面我们也提过了,在Hero类中,我们需要定义一个备忘录内部类。这个备忘录类也挺简单,就是保存了一个HeroInfo,但注意备忘录类要实现一个空接口。Hero类提供了创建和还原备忘录的方法。

 

为了保持代码完整性,还是把空接口代码贴出来。

 

package com.ittianyu.memento;public interface MementoIF {}


然后是HeroInfo类,这个类也很简单。


package com.ittianyu.memento;public class HeroInfo {private String state;private int health;public String getState() {return state;}public void setState(String state) {this.state = state;}public int getHealth() {return health;}public void setHealth(int health) {this.health = health;}public HeroInfo() {this("normal", 100);}public HeroInfo(String state, int health) {super();this.state = state;this.health = health;}@Overridepublic String toString() {return "HeroInfo [state=" + state + ", health=" + health + "]";}}

 


管家非常简单,就是存取备忘录。当然这个管家类只存了一个备忘录,如果是要支持多备忘录的话,就会麻烦一点。

 

写到这里,所有代码都写完了。

 

测试结果:

HeroInfo [state=normal, health=100]

HeroInfo [state=dead, health=0]

HeroInfo [state=normal, health=100]

 

 

3 在什么情况下使用备忘录模式

 

 * 需要对对象进行保存快照的情况。

 

 

4 备忘录模式的优点和缺点

 

优点:

 * 备忘录可以把复杂的发起人内部信息对其他的对象屏蔽起来,从而可以恰当地保持封装的边界。

 * 简化了发起人类。

 * 当发起人角色的状态改变时,有可能这个状态无效,这个时候就可以使用存储起来的备忘录恢复状态。

 

缺点:

 * 如果发起人角色的状态需要完整地存储到备忘录对象中,那么在资源消耗上面备忘录对象会很昂贵。

 * 当负责人角色将一个备忘录存储起来的时候,负责人可能并不知道这个状态会占用多少存储空间,从而无法提醒用户一个操作是否会很昂贵。

 * 当发起人角色的状态改变的时候,有可能这个状态无效。如果状态改变的成功率不高的话,不如采取“假如”协议模式。


1 0
原创粉丝点击