首页 > 其他分享 >【设计模式】备忘录模式Memento - 在聊天会话中记录历史消息

【设计模式】备忘录模式Memento - 在聊天会话中记录历史消息

时间:2023-09-11 20:33:00浏览次数:36  
标签:String 备忘录 对象 public Blog 会话 设计模式 id Memento

(目录)


相较于其他设计模式,备忘录模式不算太常用,但好在这个模式理解、掌握起来并不难,代码实现也比较简单,应用场景更是比较明确和有限。 一般应用于编辑器或会话上下文中防丢失、撤销、恢复等场景中。

模式原理分析

备忘录模式的原始定义是:

捕获并外部化对象的内部状态,以便以后可以恢复,所有这些都不会违反封装。

这个定义是非常简单的,备忘录模式对应的 UML 图: image.png

从上面 UML 图中,备忘录模式包含两个关键角色。

  • 原始对象(Originator)
  1. 创建自身所需要的属性和业务逻辑;
  2. 提供方法 create() 和 restore(memento) 来保存和恢复对象副本。
  • 备忘录(Memento):用于保存原始对象所有属性状态,以便在未来进行撤销操作。

首先,创建原始对象 Originator对象中有四个属性,分别:

  • state 用于显示当前对象状态;
  • id、name、phone 用来模拟业务属性;
  • 并添加 get、set 方法、create() 方法用于创建备份对象,restore(memento) 用于恢复对象状态
public class Originator {

    private String state = "原始对象";

    private String id;
    private String name;
    private String phone;

    public Originator(){}

    public Memento create() {
        return new Memento(id, name, phone);
    }

    public void restore(Memento m) {
        this.state = m.getState();
        this.id = m.getId();
        this.name = m.getName();
        this.phone = m.getPhone();
    }

    @Override
    public String toString() {
        return "Originator{" +
                "state='" + state + '\'' +
                ", id='" + id + '\'' +
                ", name='" + name + '\'' +
                ", phone='" + phone + '\'' +
                '}';
    }
}

然后,再来创建备忘录对象 Memento,备忘录对象几乎就和原始对象的属性一模一样。

public class Memento {
    private String state = "从备份对象恢复为原始对象";  // 打印当前状态
    private String id;
    private String name;
    private String phone;

    public Memento(String id, String name, String phone) {
        this.state = state;
        this.id = id;
        this.name = name;
        this.phone = phone;
    }

    @Override
    public String toString() {
        return "Memento{" +
                "state='" + state + '\'' +
                ", id='" + id + '\'' +
                ", name='" + name + '\'' +
                ", phone='" + phone + '\'' +
                '}';
    }
}

接着运行一个单元测试,如下代码:

package cn.happymaya.ndp.behavioral.memento;

public class Demo {
    public static void main(String[] args) {
        Originator originator = new Originator();
        originator.setId("1");
        originator.setName("mickjoust");
        originator.setPhone("12345678900");
        System.out.println(originator);

        Memento memento = originator.create();
        originator.setName("修改");
        System.out.println(originator);

        originator.restore(memento);
        System.out.println(originator);
    }
}

// 输出结果
// Originator{state='原始对象', id='1', name='mickjoust', phone='12345678900'}
// Originator{state='原始对象', id='1', name='修改', phone='12345678900'}
// Originator{state='从备份对象恢复为原始对象', id='1', name='mickjoust', phone='12345678900'}

从上面的代码实现和最后输出的结果可以看出,备忘录模式代码实现非常简单的,关键点就在于要能保证原始对象在某一个时刻的对象状态被完整记录下来


使用场景

备忘录模式经常使用的场景以下两种:

  1. 需要保存一个对象在某一个时刻状态时。比如,在线编辑器中编写文字时断网,需要恢复为断网前的状态;
  2. 不希望外界直接访问对象的内部状态时。比如,在包裹配送的过程中,如果从仓库运送到配送站,只需要显示“在运行中”,而具体使用汽车还是飞机,这个用户并不需要知道。

通过一个栗子演示一下,假设现在正在管理一个博客系统,经常需要创建 Blog 对象,但是有一些作者在写 Blog 的过程中可能会出现断网的情况,这时就需要保存 Blog 对象在这个时刻的状态信息,后续等作者重新联网后,能够及时地恢复其断网前的状态。那具体该怎么来做呢? 首先,创建一个 Blog 对象,该对象中包含 id、title 和 content,分别代表了 Blog 的唯一编号、标题和内容;并提供创建备忘录的 createMemento() 和 restore(BlogMemento m) 方法,分别用于创建备忘录和通过备忘录来恢复原始的 Blog 对象。

public class Blog {
    private Long id;
    private String title;
    private String content;

    public Blog(Long id, String title) {
        this.id = id;
        this.title = title;
    }
    
    // 省略 getter/setter

    public BlogMemento createMemento() {
        return new BlogMemento(id, title, content);
    }

    public void restore(BlogMemento m) {
        this.id = m.getId();
        this.title = m.getTitle();
        this.content = m.getContent();
    }

    @Override
    public String toString() {
        return "Blog{" +
                "id=" + id +
                ", title='" + title + '\'' +
                ", content='" + content + '\'' +
                '}';
    }
}

然后,创建一个 Blog 的备忘录对象 BlogMemento,同样是复制 Blog 所需要的所有属性内容。

public class BlogMemento {

    private final long id;
    private final String title;
    private final String content;

    public BlogMemento(long id, String title, String content) {
        this.id = id;
        this.title = title;
        this.content = content;
    }

    public long getId() {
        return id;
    }

    public String getTitle() {
        return title;
    }

    public String getContent() {
        return content;
    }
}

这样基于 Blog 对象的备忘录就创建好了。最后,依然运行一段单元测试代码来看看运行结果。

package cn.happymaya.ndp.behavioral.memento;

public class Client {

    public static void main(String[] args) {
        Blog blog = new Blog(1L, "My Blog");
        blog.setContent("ABC");                         // 原始文章
        System.out.println(blog);

        BlogMemento memento = blog.createMemento();     // 创建 blog 的备忘录
        blog.setContent("123");                         // 改变内容
        System.out.println(blog);

        blog.restore(memento);                          // 撤销操作
        System.out.println(blog);                       // 这时显示原始的内容
    }
}

// 输出结果
Blog{id=1, title='My Blog', content='ABC'}
Blog{id=1, title='My Blog', content='123'}
Blog{id=1, title='My Blog', content='ABC'}

从运行结果中,可以发现使用备忘录模式能非常方便地进行撤销操作。编辑文章内容时,其实就是在修改 content 内容,这时备忘录会记录特定时间点里的对象状态,如果这时需要撤销修改,那么就会恢复到原来的对象状态。所以说,备忘录模式在频繁需要撤销与恢复的场景中能够发挥很好的作用


备忘录模式

使用备忘录模式的原因,可总结为以下两个。

  1. **为了记录多个时间点的备份数据。**与传统备份不同的是,备忘录模式更多是用来记录多个时间点的对象状态数据,比如编辑器、聊天会话中会涉及多次操作和多次交互对话,一方面是为了记录某个时间点数据以便以后运营用来做数据分析,另一方面是为了能够通过多次数据快照,防止客户端篡改数据;
  2. 需要快速撤销当前操作并恢复到某对象状态。 比如,微信中的撤回功能其实就是备忘录模式的一种体现,用户发错信息后,需要立即恢复到未发送状态。

优缺点

通过上述分析,备忘录模式,主要有以下优点:

  1. 能够快速撤消对对象状态的更改。 比如,在编辑器中不小心覆盖了一段重要的文字,使用 undo 操作能够快速恢复这段文字;
  2. 能够帮助缓存记录历史对象状态。比如,在客服会话聊天中,对于某一些重要的对话(退换货、价保等),我们会记录这些对象数据,传统的做法是调用一次服务接口,一旦服务出现故障就很容易导致聊天回复速度变慢;而使用备忘录模式则能够记录这些重要的数据信息(用户提供的订单数据),而不需要反复查询接口,这样能提升回复客户的效率。
  3. 能够提升代码的扩展性。 备忘录模式是通过增加外部对象来保存原始对象的状态,而不是在原始对象中新增状态记录,当不再需要保存对象状态时就能很方便地取消这个对象。同理,新增备忘录对象也非常容易。

同样,备忘录模式也有一些缺点:

  1. 备忘录会破坏封装性。 因为当备忘录在进行恢复的过程中遇见错误时,可能会恢复错误的状态。比如,备份的对象状态中有需要调用数据库等外部服务时,在恢复过程中如果遇见数据库宕机,那么可能恢复的对象数据就会存在错误;
  2. 备忘录的对象数据很大时,读取数据可能出现内存用尽的情况。 比如,在编辑器中加入高清的图片,如果直接记录图片本身可能会导致内存被用尽,进而导致系统出现崩溃的情况。

总结

备忘录模式也叫快照模式。具体来说,就是通过捕获对象在某一个时刻对象状态,再将其保存到外部对象,以便在需要的时候恢复对象到指定时刻下的状态。 **备忘录模式的应用场景比较局限,主要是用来备份、撤销、恢复等,**这与平时常说的“备份”看上去很相似,但实际上两者的差异是很大的:

  • 备忘录模式更侧重于代码设计和实现,支持简单场景中的应用,比如记录 Web 请求中的 header 信息等;
  • 备份更多是以解决方案的形式出现,比如异地容灾备份、数据库主从备份等所支持的是更复杂的业务场景,备份的数据量往往也更大。

因此,在使用备忘录模式时,莫要认为它就是万能的备份模式,要合理评估对象所使用的内存空间,再确定是否使用备忘录模式。

标签:String,备忘录,对象,public,Blog,会话,设计模式,id,Memento
From: https://blog.51cto.com/panyujie/7438168

相关文章

  • 如何保持 SSH 会话不中断?
    哈喽大家好,我是咸鱼不知道小伙伴们有没有遇到过下面的情况:使用终端(XShell、secureCRT或MobaXterm等)登录Linux服务器之后如果有一段时间没有进行交互,SSH会话就会断开如果正在执行一些非后台命令,SSH会话断开就可能会使得这些命令可能会被中断,导致任务无法完成那如何保持......
  • 设计模式--禅
    设计模式视频一、六大设计原则1、单一职责一个方法尽可能做一件事情单一职责原则(SingleResponsibilityPrinciple,SRP)2、接口隔离原则尽量依赖最小的接口六大设计模式原则-接口隔离原则3、依赖倒置原则:高层模块不应该依赖低层模块(基础模块),两者都应该依赖其抽象,传递的是抽象抽象不应......
  • 4. 前端设计模式之原型模式
    PrototypePattern原型模式:在多个对象间共享相同的属性  JavaScript原生支持原型链也是实现继承的基础,如以下代码虽然是使用的ES2016新的语法classc创建的类Dog,然后又使用new实例化对象dog1、dog2、dog3,底层依然操作的原型链:classDog{constructor(name){this.name......
  • 设计模式-中介者模式
    中介者模式定义+用一个中介对象来封装一系列的对象交互。中介者使各个对象不需要显示地互相引用,从而使耦合松散,并且可以独立地改变他们的交互。UML类图使用场景+一般用于一组对象以定义良好但复杂的方式进行通信的场景,比如窗体的Form或者web页面asp+想定制一个分......
  • 趣解设计模式之《办理入职这么难吗?》
    〇、小故事小王大学毕业,经过大学期间的刻苦学习,终于成功的面试上了一家公司。按照约定的时间,小王兴高采烈的来到公司前台去办理入职手续。“您好,我是来办理入职手续的”小王对前台妹妹说,前台妹妹看都没看他一眼,顺手递给了他一个单子,带着一丝不耐烦的语气跟他说,“照着单子做就行”......
  • 适配器设计模式解决接口冲突
    title:适配器设计模式解决接口冲突index_img:https://tuchuangs.com/imgs/2023/08/04/f341f43b9362c8a1.pngtags:-JavaSE-接口categories:-JavaSEhide:falseexcerpt:适配器设计模式解决接口冲突问题描述当一个接口中抽象方法过多,但是我只要使用其中一部......
  • 【设计模式】命令模式Command:在一次请求中封装多个参数
    (目录)命令模式使用频率不算太高。如果熟悉函数式编程的话,会发现命令模式完全没有使用的必要,甚至在业务开发的场景中也很少使用到。不过对于想要找到正确抽象的设计者来说,命令模式的设计思想却非常值得借鉴。命令和查询的区别:查询,获取一个不可变的结果;命令,改变状态,不一定获......
  • java设计模式,简单工厂和抽象工厂有什么区别?
    java设计模式,简单工厂和抽象工厂有什么区别?简单工厂模式:这个模式本身很简单而且使用在业务较简单的情况下。一般用于小项目或者具体产品很少扩展的情况(这样工厂类才不用经常更改)。它由三种角色组成:工厂类角色:这是本模式的核心,含有一定的商业逻辑和判断逻辑,根据逻辑不同,产生具体的工......
  • 设计模式-单例模式
    保证在整个软件系统中,对某个类只能存在一个对象实例。饿汉式(类加载时创建,没用到也创建)1、构造器私有化(防止new对象)。2、类内部创建私有的静态对象。3、用一个公共的getInstance()静态方法返回该对象。如Runtime类懒汉式(使用才创建)1、仍然使构造器私有化。2、类内部定义静......
  • 软件设计模式系列之二———抽象工厂模式
    1抽象工厂模式的定义抽象工厂模式是一种创建型设计模式,它提供了一种创建一组相关或相互依赖对象的方式,而无需指定它们的具体类。该模式以一组抽象接口为核心,包括抽象工厂接口和一组抽象产品接口,每个具体工厂类负责创建特定产品家族,保证这些产品之间的兼容性。客户端代码通过与抽......