设计模式--备忘录模式

备忘录模式(Memento Pattern)

备忘录模式在不破坏封闭的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可以将该对象恢复到之前保存的状态。大类划分为行为型模式。
使用备忘录模式可以避免暴漏原有的发起者对象的内部信息,而该信息又需要保存在外部。备忘录模式把可能很复杂的对象内部信息对其他对象封闭起来,从而保证了发起者封装的边界,有效避免了因保存在外部数据的修改而对发起者对象内部造成影响。

介绍

意图:在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。
角色
1. 发起人角色(Originator):发起人角色在要存储当前状态时,产生一个备忘录;在需要恢复之前的状态时,则接收外部备忘录对象用来恢复状态。
2. 备忘录角色(Memento):备忘录角色用来保存某一时刻发起者对象的内部状态信息,但是需要保证不能将其保存的信息泄露给其他对象。
3. 管理者角色(CareTaker):管理者角色用来保存发起者对象产生的备忘录实例,然后在一个合适的时机,发起者通知管理者,获得备忘录对象实例恢复之前的状态。
使用场景
1. 需要在某一时刻恢复一个对象先前的状态时;
2. 需要在外部保存对象某一时刻的状态,但如果用一个接口来让其他对象直接获得这些状态,将会暴漏对象的实现细节并破坏对象的封装性。
优点
1. 给用户提供了一种可以恢复状态的机制,可以使用户能够比较方便地回到某个历史的状态。
2. 实现了信息的封装,使得用户不需要关心状态的保存细节。
缺点:消耗资源。如果类的成员变量过多,势必会占用比较大的资源,而且每一次保存都会消耗一定的内存。
注意事项
1. 为了符合迪米特原则,还要增加一个管理备忘录的类。
2. 为了节约内存,可使用原型模式+备忘录模式。

实现

本例中,我们模拟一个Hero挑战Boss的游戏场景,Hero有三次机会挑战Boss,如果三次挑战不成功,则恢复到上个状态。

步骤 1

我们先定义一个备忘录接口,这个接口主要的目的是为了系统安全。
INarrowMemento.java

package com.mrcsj.test.study.memento;


/**
 * @author admin
 * @version 1.0
 * @created 22-十一月-2016 13:40:01
 */
public interface INarrowMemento {

}

步骤 2

定义一个备忘录管理者,用来存储Hero对象的初始状态。
CareTaker.java

package com.mrcsj.test.study.memento;


/**
 * 备忘录管理者
 * @author admin
 * @version 1.0
 * @created 22-十一月-2016 13:39:54
 */
public class CareTaker {

    private INarrowMemento memento;

    public CareTaker(){

    }

    public void finalize() throws Throwable {

    }
    public INarrowMemento getMemento(){
        return memento;
    }

    /**
     * 
     * @param memento
     */
    public void setMemento(INarrowMemento memento){
        this.memento = memento;
    }
}//end CareTaker 

步骤 3

定义Hero对象,内部有一个实现了INarrowMemento的内部类,该内部类即是备忘录。
Hero.java

package com.mrcsj.test.study.memento;

import java.util.Random;

/**
 * @author admin
 * @version 1.0
 * @created 22-十一月-2016 13:39:58
 */
public class Hero {

    private int blood;
    private int sword;

    public Hero(){
        this.blood = 100;
        this.sword = 100;
    }

    public void finalize() throws Throwable {

    }
    public INarrowMemento CreateMemento(){
        System.out.println("create memento");
        return new Memento(this.blood, this.sword);
    }

    /**
     * 挑战Boss
     * @return
     */
    public int koBoss(){
        if(this.blood <= 0 || this.sword <= 0) {
            System.out.println("挑战失败");
            return -1;
        }else {
            double win = Math.random();
            if(win <= 0.02) {
                System.out.println(this.toString());
                System.out.println("恭喜你挑战成功!");
                return 1;
            }else {
                Random random = new Random();
                System.out.println(this.toString()); 
                System.out.println("继续攻击boss");
                int blood_sub = random.nextInt(10);
                int sword_sub = random.nextInt(10);
                this.blood -= blood_sub;
                this.sword -= sword_sub;
                return 0;
            }
        }
    }


    @Override
    public String toString() {
        return "当前血液:" + this.blood + ", 当前武力值:" + this.sword;
    } 
    /**
     * 
     * @param memento
     */
    public void restoreFromMemento(INarrowMemento memento){
        System.out.println("恢复备忘录中的状态");
        if(memento != null) {
            Memento memTmp = (Memento) memento;
            this.blood = memTmp.getBlood();
            this.sword = memTmp.getSword();
        }
    }

    private class Memento implements INarrowMemento {
        private int blood;
        private int sword;

        private Memento(int blood, int sword) {
            this.blood = blood;
            this.sword = sword;
        }

        private int getBlood() {
            return blood;
        }

        private int getSword() {
            return sword;
        }
    }
}//end Hero

步骤 4

建立客户端进行测试。
Client.java

package com.mrcsj.test.study.memento;


/**
 * @author admin
 * @version 1.0
 * @created 22-十一月-2016 13:39:56
 */
public class Client {

    public static void main(String[] args) {
        Hero hero = new Hero();
        CareTaker careTaker = new CareTaker();
        careTaker.setMemento(hero.CreateMemento());
        int cnt = 1;
        int ko = -1;
        while(ko != 1 && cnt <= 3) {
            System.out.println("第" + cnt + "次挑战");
            ko = hero.koBoss();
            while(true) {
                if(ko == -1) {
                    //挑战失败,恢复到初始状态,累加挑战次数
                    hero.restoreFromMemento(careTaker.getMemento());
                    cnt += 1;
                    break;
                } else if (ko == 0) {
                    //继续挑战
                    ko = hero.koBoss();
                } else if (ko == 1) {
                    break;
                }
            }
        }
    }
}//end Client

步骤 5

验证输出。

create memento
第1次挑战
当前血液:100, 当前武力值:100
继续攻击boss
当前血液:93, 当前武力值:93
继续攻击boss
当前血液:87, 当前武力值:91
继续攻击boss
当前血液:87, 当前武力值:86
继续攻击boss
当前血液:86, 当前武力值:81
继续攻击boss
当前血液:80, 当前武力值:74
继续攻击boss
当前血液:72, 当前武力值:70
继续攻击boss
当前血液:66, 当前武力值:61
继续攻击boss
当前血液:64, 当前武力值:57
继续攻击boss
当前血液:56, 当前武力值:53
继续攻击boss
当前血液:47, 当前武力值:50
继续攻击boss
当前血液:47, 当前武力值:44
继续攻击boss
当前血液:39, 当前武力值:41
继续攻击boss
当前血液:33, 当前武力值:39
继续攻击boss
当前血液:28, 当前武力值:34
继续攻击boss
当前血液:28, 当前武力值:27
继续攻击boss
当前血液:22, 当前武力值:23
继续攻击boss
当前血液:15, 当前武力值:20
继续攻击boss
当前血液:8, 当前武力值:15
继续攻击boss
当前血液:6, 当前武力值:11
继续攻击boss
当前血液:6, 当前武力值:10
继续攻击boss
挑战失败
恢复备忘录中的状态
第2次挑战
当前血液:100, 当前武力值:100
继续攻击boss
当前血液:92, 当前武力值:99
继续攻击boss
当前血液:89, 当前武力值:96
继续攻击boss
当前血液:81, 当前武力值:95
继续攻击boss
当前血液:76, 当前武力值:86
继续攻击boss
当前血液:72, 当前武力值:86
继续攻击boss
当前血液:65, 当前武力值:79
继续攻击boss
当前血液:63, 当前武力值:73
继续攻击boss
当前血液:58, 当前武力值:64
继续攻击boss
当前血液:51, 当前武力值:64
恭喜你挑战成功!
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值