手记

java设计模式-备忘录模式

最浪费时间的三件事情:choose,hesitate,regret.做了一件事情之后却开始后悔,但是时间无法倒流。

软件却不一样。下棋时候的悔棋,编写文档的撤销,查看网页时的后退,这些频繁却相对简单的操作不需要存储在磁盘中,只需要读取一下内存中的状态。

备忘录模式:在不破坏封装的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,这样以后就可以将该对象恢复至原先保存的状态。
结构图如下:
Originator(发起人):负责创建一个备忘录Memetro,用以记录当前时刻它的内部状态,并可使用备忘录恢复内部状态。Originator可根据需要决定Memento存储Originator的哪些内部状态。
Memento(备忘录):负责存储Originator对象的内部状态,并可防止除Originator之外的对象访问Memento。备忘录有两个接口:Caretaker只能看到备忘录的窄接口,只能将备忘录传递给其他对象;Originator能够看到一个宽接口,允许他访问返回到之前状态所需的全部数据。
Caretaker(管理者):负责保存好备忘录Memento,不能对备忘录中的内容进行操作和检查。

现在描述一个简单的场景,游戏中一个角色有生命值,攻击力,防御力等数值,在决斗boss前和后肯定不一样,如果决斗boss后状态不理想允许玩家恢复游戏到决斗前。

代码的结构图如下:

对应的类分别入下。
Originator(游戏角色):

package com.hy.memento;

public class Originator implements Cloneable{

    private int healthy;
    private int denfence;
    private int attack;

    public int getHealthy() {
        return healthy;
    }

    public void setHealthy(int healthy) {
        this.healthy = healthy;
    }

    public int getDenfence() {
        return denfence;
    }

    public void setDenfence(int denfence) {
        this.denfence = denfence;
    }

    public int getAttack() {
        return attack;
    }

    public void setAttack(int attack) {
        this.attack = attack;
    }

    public void birth() {
        this.setAttack(100);
        this.setDenfence(100);
        this.setHealthy(100);
        showState();
    }

    public void fight() {
        this.setAttack(10);
        this.setDenfence(10);
        this.setHealthy(10);
        showState();
    }

    public Memento saveStates() {
        try {
            Originator record = (Originator) this.clone();
            return new Memento(record);
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return null;
    }

    public void setMemento(Memento memento) {
        this.setAttack(memento.getAttack());
        this.setDenfence(memento.getDenfence());
        this.setHealthy(memento.getHealthy());
    }

    public void showState() {
        System.out.println("当前生命值" + this.healthy + "攻击力" + this.attack + "防御力"
                + this.denfence);
    }
}

Memento(备份):

package com.hy.memento;

public class Memento {

    private int healthy;
    private int denfence;
    private int attack;

    public Memento(Originator originator){
        setHealthy(originator.getHealthy());
        setDenfence(originator.getDenfence());
        setAttack(originator.getAttack());
    }
    public int getDenfence() {
        return denfence;
    }

    public void setDenfence(int denfence) {
        this.denfence = denfence;
    }

    public int getAttack() {
        return attack;
    }

    public void setAttack(int attack) {
        this.attack = attack;
    }

    public int getHealthy() {
        return healthy;
    }

    public void setHealthy(int healthy) {
        this.healthy = healthy;
    }

}

Caretaker(备份管理):

package com.hy.memento;

public class Caretaker {
    private Memento memento;

    public Memento getMemento() {
        return memento;
    }

    public void setMemento(Memento memento) {
        this.memento = memento;
    }

}

最后的测试类:

package com.hy.memento;

public class TestMemento {
    public static void main(String[] args) {
        Originator originator = new Originator();
        Caretaker ct = new Caretaker();
        originator.birth();
        ct.setMemento(originator.saveStates());
        originator.fight();
        originator.setMemento(ct.getMemento());
        originator.showState();
    }
}

测试结果如下:

这里做的是用户角色完全读档存档,如果是部分存档和归档,可以在Memento中做些修改只针对某些属性做存取。类似于网游,将一些不太敏感的数据存放本地,一些重要的数据存放在远程服务器。每次重新登录的时候只从远程服务器加载重要的数据,如金币、装备等,其他的例如在线时间、短期buf,存放在本地管理。

memento模式适合功能比较复杂的,但需要维护或记录属性历史的类,或者需要保存的属性只是众多属性的一部分的时候,Originator可以根据Memento保存的信息恢复到前一状态。
当角色的状态发生改变时,这个状态可能无效,这个时候就可以使用暂时存储起来的备忘录将状态还原。
命令模式中有撤销功能,可以使用备忘录模式来存储可撤销操作的状态。

备忘录模式的优缺点如下:
优点--
1、发起人备份自己的状态不需要自己管理,可以备份到外部,这样可以很好的保持封装的边界,这样做的意义在于可以给外部提供一个简单的操作该对象内部状态的接口。保持封装的边界这应该算是最重要的优点了。
2、发起人状态的备份与恢复,发起人自身不需要管理与操作,而是由客户端自行按需要处理。
3、如果发起人的状态出现问题,可以很轻松的恢复。
缺点--
1、如果全部备份发起人的状态,或者其中有占用内存较大的属性(比如一个长数组),则会让备忘录模式的使用代价昂贵。
2、由于备份的信息是由发起人自己提供的,所以管理者无法预知备份的信息的大小,所以在客户端使用时,可能一个操作占用了很大的内存,但客户端并不知晓。
3、当发起人的状态改变的时候,有可能这个状态无效。如果状态改变的成功率不高的话,可以采取“假如”协议模式。“假如”的意思是指,我们将一直假如状态的改变会失败,从而对此做出一系列准备的工作。不过很明显,如果状态改变的成功率很高,则这样做的收益甚微。

1人推荐
随时随地看视频
慕课网APP

热门评论

一直对设计模式不太熟

查看全部评论