浅谈设计模式 - 备忘录模式(十五)

简介: 浅谈设计模式 - 备忘录模式(十五)

前言


备忘录这个模式用的比较少,基本学完就可以忘记的一个模式,但是应用的情况还是不少,同时这个模式几乎“人手必备”,那就是典型的ctrl+z这个功能就可以看作备忘录的典型案例,我们的游戏存档也可以看作是一种备忘录的变形。


定义


备忘录模式重点在于记录住对象的状态,可以让对象的状态回到上一次操作的时候,同时可以保证不破坏封装性的前提下存在于其他的对象,这个模式需要注意的一个点是为了管理备忘的类,需要一个“辅助类”来进行备忘内容的统一管理,就好比我们的游戏的存档和读档一样。


应用场景


  1. 游戏的存档
  2. 恢复上一次状态
  3. 文字撤销
  4. 还原


优缺点:


  1. 不破坏对象的封装性保存对象的状态,可以让对象回到过去定义的任何一种状态。
  2. 用户不需要关系备忘的内容以及如何备忘,只需要关注备忘的数据即可。
  3. 备忘的数据过多会造成卡顿,并且有可能存在备忘记录丢失的可能性。


结构图:



备忘录模式主要分为三个点,第一:需要定义和备忘的内容有关的对外接口,并且将请求转发给具体的备忘器,然后备忘器备忘数据存储到具体的备忘实现对象来保存对象的状态,第二:定义备忘录存储接口传递对象的状态,记录当前的工作数据,第三需要使用具体的记录器恢复器对象来恢复具体的对象数据。

了解基本的设计对象之后下面来看下具体的实现结构图,这种模式有两种实现的结构方式,一种是嵌套类的结构,另一种是使用中间接口委托的形式进行处理,但是不管是嵌套结构还是中间接口委托的形式,基本都包含了下面的接口:

Memento(备忘录):包含了要被恢复的对象的状态,是快照的具体存储位置。

Originator(原发器):创建并在 Memento 对象中存储状态,可以看作是需要被快照的对象,通常包含某些状态数据。

Caretake(恢复器):负责从 Memento 中恢复对象的状态,可以看作是一个记录提取器或者说恢复器。


嵌套结构


嵌套结构的结构图如下所示,嵌套的结构下通常会把原发器嵌入到备忘录的接口里面,在部分的时候通过备忘录把数据进行恢复或者存储,恢复器负责对于原发器的状态进行还原的操作:


网络异常,图片无法展示
|


中间接口结构



中间接口结构针对一些不能使用嵌套类的情况,这时候我们只能委托给子类完成备份数据的处理,当然这样也有一种灵活性,就是备份的方式可以多样化。


网络异常,图片无法展示
|


案例代码


下面我们来看下上面的结构图的相关代码,由于这里使用的是java代码,所以我们直接使用嵌套类的形式完成:

Originator(原发器):创建并在 Memento 对象中存储状态,可以看作是需要被快照的对象,通常包含某些状态数据。


public class Originator {
    private String state;
    public Originator() {
    }
    public Originator(Memento memento) {
        memento = new Memento(state);
    }
    public String getState() {
        return state;
    }
    public void setState(String state) {
        this.state = state;
    }
}


Memento(备忘录):包含了要被恢复的对象的状态,是快照的具体存储位置。


public class Memento {
    /**
     * 需要被备份的状态
     */
    private String state;
    public Memento(String state) {
        this.state = state;
    }
    public String getState() {
        return state;
    }
}


Caretake(恢复器):负责从 Memento 中恢复对象的状态,可以看作是一个记录提取器或者说恢复器。


public class CareTaker {
    private Memento memento;
    public CareTaker(Memento memento) {
        this.memento = memento;
    }
    public Memento getMemento() {
        return memento;
    }
    public void setMemento(Memento memento) {
        this.memento = memento;
    }
}


Client:最终的执行客户端,通过调用CareTaker进行将数据的状态进行还原:


public class Client {
    private Originator originator;
    public Client(Originator originator) {
        this.originator = originator;
    }
    /**
     * 恢复记录的状态
     */
    public void restore(CareTaker careTaker) {
        originator.setState(Optional.ofNullable(careTaker)
                .map(CareTaker::getMemento)
                .map(Memento::getState)
                .orElse("off"));
    }
}


最后是单元测试:


public class Main {
    public static void main(String[] args) {
        Originator originator = new Originator();
        originator.setState("on");
        System.out.println("备份状态"+ originator.getState());
        Memento memento = new Memento(originator.getState());
        CareTaker careTaker = new CareTaker(memento);
        //修改状态
        originator.setState("test");
        System.out.println("修改状态"+ originator.getState());
        Client client = new Client(originator);
        client.restore(careTaker);
        System.out.println("还原状态"+ originator.getState());
    }/*运行结果:
        备份状态on
        修改状态test
        还原状态on
    */
}


实际案例


不管是操作系统还是文本编辑,备忘和撤销的功能是必不可少的,所以我们用文本编辑器来讲述备忘录模式。最常见的情况是我们写好文章写到一半的时候关闭软件下次进入依然有原来的数据。这里我们使用“特殊方法”代替ctrl+s来进行数据的备份操作。

代码的内容和案例内容类似,只不过由于使用了对象需要注意对象的潜拷贝和深拷贝的问题。

首先我们建立文本对象:


public class Text {
    /**
     * 文本信息
     */
    private String info;
    public String getInfo() {
        return info;
    }
    public void setInfo(String info) {
        this.info = info;
    }
}


下面是一个文本编辑器:


public class TextEditor {
    private Text text;
    public TextEditor(Text text) {
        this.text = text;
    }
    public void write(String info){
        text.setInfo(info);
    }
    public Text getText() {
        return text;
    }
    public void setText(Text text) {
        this.text = text;
    }
}


备忘录需要注意备份对象需要使用深拷贝


public class Memento {
    private Text text;
    public Text getText() {
        return text;
    }
    public void setText(Text text) {
        String info = text.getInfo();
        Text newText = new Text();
        newText.setInfo(info);
        this.text = newText;
    }
}


恢复器,最后要使用恢复器进行数据的恢复操作


public class CareTaker {
    private Memento memento;
    public CareTaker(Memento memento) {
        this.memento = memento;
    }
    /**
     * 恢复记录的状态
     */
    public void restore(TextEditor textEditor) {
        textEditor.getText().setInfo(memento.getText().getInfo());
    }
    public Memento getMemento() {
        return memento;
    }
    public void setMemento(Memento memento) {
        this.memento = memento;
    }
}


最后是测试代码:


public class Main {
    public static void main(String[] args) {
        Text text = new Text();
        TextEditor textEditor = new TextEditor(text);
        textEditor.write("d111111");
        Memento memento = new Memento();
        memento.setText(text);
        CareTaker careTaker = new CareTaker(memento);
        textEditor.write("66666");
        careTaker.restore(textEditor);
        System.out.println(careTaker.getMemento().getText().getInfo());
    }/*运行结果:
        浅拷贝:66666
        深拷贝:d111111
    */
}


总结



这个设计模式用的真的并不算很多,所以简单过一下即可。


写在最后



算是比较有意思的设计模式。

相关文章
|
3天前
|
搜索推荐 编译器 Linux
一个可用于企业开发及通用跨平台的Makefile文件
一款适用于企业级开发的通用跨平台Makefile,支持C/C++混合编译、多目标输出(可执行文件、静态/动态库)、Release/Debug版本管理。配置简洁,仅需修改带`MF_CONFIGURE_`前缀的变量,支持脚本化配置与子Makefile管理,具备完善日志、错误提示和跨平台兼容性,附详细文档与示例,便于学习与集成。
271 116
|
18天前
|
域名解析 人工智能
【实操攻略】手把手教学,免费领取.CN域名
即日起至2025年12月31日,购买万小智AI建站或云·企业官网,每单可免费领1个.CN域名首年!跟我了解领取攻略吧~
|
12天前
|
安全 Java Android开发
深度解析 Android 崩溃捕获原理及从崩溃到归因的闭环实践
崩溃堆栈全是 a.b.c?Native 错误查不到行号?本文详解 Android 崩溃采集全链路原理,教你如何把“天书”变“说明书”。RUM SDK 已支持一键接入。
662 219
|
5天前
|
数据采集 人工智能 自然语言处理
Meta SAM3开源:让图像分割,听懂你的话
Meta发布并开源SAM 3,首个支持文本或视觉提示的统一图像视频分割模型,可精准分割“红色条纹伞”等开放词汇概念,覆盖400万独特概念,性能达人类水平75%–80%,推动视觉分割新突破。
344 34
Meta SAM3开源:让图像分割,听懂你的话
|
10天前
|
人工智能 移动开发 自然语言处理
2025最新HTML静态网页制作工具推荐:10款免费在线生成器小白也能5分钟上手
晓猛团队精选2025年10款真正免费、无需编程的在线HTML建站工具,涵盖AI生成、拖拽编辑、设计稿转代码等多种类型,均支持浏览器直接使用、快速出图与文件导出,特别适合零基础用户快速搭建个人网站、落地页或企业官网。
1548 157
|
存储 人工智能 监控
从代码生成到自主决策:打造一个Coding驱动的“自我编程”Agent
本文介绍了一种基于LLM的“自我编程”Agent系统,通过代码驱动实现复杂逻辑。该Agent以Python为执行引擎,结合Py4j实现Java与Python交互,支持多工具调用、记忆分层与上下文工程,具备感知、认知、表达、自我评估等能力模块,目标是打造可进化的“1.5线”智能助手。
897 61
|
7天前
|
编解码 Linux 数据安全/隐私保护
教程分享免费视频压缩软件,免费视频压缩,视频压缩免费,附压缩方法及学习教程
教程分享免费视频压缩软件,免费视频压缩,视频压缩免费,附压缩方法及学习教程
295 140