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

在这里插入图片描述

点赞再看,养成习惯,公众号搜一搜【一角钱技术】关注更多原创技术文章。
本文 GitHub org_hejianhui/JavaStudy 已收录,有我的系列文章。

前言

23种设计模式快速记忆的请看上面第一篇,本篇和大家一起来学习备忘录模式相关内容。

模式定义

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

模版实现如下

package com.niuh.designpattern.memento.v1;

/**
 * <p>
 * 备忘录模式
 * </p>
 */
public class MementoPattern {
    public static void main(String[] args) {
        Originator or = new Originator();
        Caretaker cr = new Caretaker();
        or.setState("S0");
        System.out.println("初始状态:" + or.getState());
        cr.setMemento(or.createMemento()); //保存状态      
        or.setState("S1");
        System.out.println("新的状态:" + or.getState());
        or.restoreMemento(cr.getMemento()); //恢复状态
        System.out.println("恢复状态:" + or.getState());
    }
}

//备忘录
class Memento {
    private String state;

    public Memento(String state) {
        this.state = state;
    }

    public void setState(String state) {
        this.state = state;
    }

    public String getState() {
        return state;
    }
}

//发起人
class Originator {
    private String state;

    public void setState(String state) {
        this.state = state;
    }

    public String getState() {
        return state;
    }

    public Memento createMemento() {
        return new Memento(state);
    }

    public void restoreMemento(Memento m) {
        this.setState(m.getState());
    }
}

//管理者
class Caretaker {
    private Memento memento;

    public void setMemento(Memento m) {
        memento = m;
    }

    public Memento getMemento() {
        return memento;
    }
}

输出结果如下

初始状态:S0
新的状态:S1
恢复状态:S0

解决的问题

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

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

模式组成

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

组成(角色)作用
发起人(Originator)角色记录当前时刻的内部状态信息,提供创建备忘录和恢复备忘录数据的功能,实现其他业务功能,它可以访问备忘录里的所有信息。
备忘录(Memento)角色负责存储发起人的内部状态,在需要的时候提供这些内部状态给发起人。
管理者(Caretaker)角色对备忘录进行管理,提供保存与获取备忘录的功能,但其不能对备忘录的内容进行访问与修改。

实例说明

实例概况

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

使用步骤

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

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;
    }
}

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

class GameRole {

    private int vit;    //生命力
    private int atk;    //攻击力
    private int 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;
    }

    //状态显示
    public void stateDisplay() {
        System.out.println("角色当前状态:");
        System.out.println("体力:" + this.vit);
        System.out.println("攻击力:" + this.atk);
        System.out.println("防御力: " + this.def);
        System.out.println("-----------------");
    }

    //获得初始状态
    public void getInitState() {
        this.vit = 100;
        this.atk = 100;
        this.def = 100;
    }

    //战斗后
    public void fight() {
        this.vit = 0;
        this.atk = 0;
        this.def = 0;
    }

    //保存角色状态
    public RoleStateMemento saveState() {
        return (new RoleStateMemento(vit, atk, def));
    }

    //恢复角色状态
    public void recoveryState(RoleStateMemento memento) {
        this.vit = memento.getVit();
        this.atk = memento.getAtk();
        this.def = memento.getDef();
    }
}

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

class RoleStateCaretaker {

    private RoleStateMemento memento;

    public RoleStateMemento getMemento() {
        return memento;
    }

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

步骤4:测试输出

public class MementoPattern {

    // 逻辑大致为打boss前存档,打boss失败了
    public static void main(String[] args) {
        //打boss前
        GameRole gameRole = new GameRole();
        gameRole.getInitState();
        gameRole.stateDisplay();

        //保存进度
        RoleStateCaretaker caretaker = new RoleStateCaretaker();
        caretaker.setMemento(gameRole.saveState());

        //打boss失败
        gameRole.fight();
        gameRole.stateDisplay();

        //恢复状态
        gameRole.recoveryState(caretaker.getMemento());
        gameRole.stateDisplay();
    }
}

输出结果

角色当前状态:
体力:100
攻击力:100
防御力: 100
-----------------
角色当前状态:
体力:0
攻击力:0
防御力: 0
-----------------
角色当前状态:
体力:100
攻击力:100
防御力: 100
-----------------

优点

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

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

缺点

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

注意事项

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

应用场景

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

模式的扩展

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

源码中的应用

#Spring
org.springframework.binding.message.StateManageableMessageContext

StateManageableMessageContext 部分源码

public interface StateManageableMessageContext extends MessageContext {

   /**
    * Create a serializable memento, or token representing a snapshot of the internal state of this message context.
    * @return the messages memento
    */
   public Serializable createMessagesMemento();

   /**
    * Set the state of this context from the memento provided. After this call, the messages in this context will match
    * what is encapsulated inside the memento. Any previous state will be overridden.
    * @param messagesMemento the messages memento
    */
   public void restoreMessages(Serializable messagesMemento);

   /**
    * Configure the message source used to resolve messages added to this context. May be set at any time to change how
    * coded messages are resolved.
    * @param messageSource the message source
    * @see MessageContext#addMessage(MessageResolver)
    */
   public void setMessageSource(MessageSource messageSource);
}

PS:以上代码提交在 Githubhttps://github.com/Niuh-Study/niuh-designpatterns.git

文章持续更新,可以公众号搜一搜「 一角钱技术 」第一时间阅读, 本文 GitHub org_hejianhui/JavaStudy 已经收录,欢迎 Star。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值