保留状态的秘密 - 深入了解Java备忘录模式(Memento Pattern)

简介: 保留状态的秘密 - 深入了解Java备忘录模式(Memento Pattern)

一、概述

1.1 介绍备忘录模式的定义和作用

备忘录模式(Memento Pattern)是一种用于保存对象状态并在需要的时候恢复的设计模式。备忘录模式属于行为型模式,在某些需要记录对象状态的场景中非常有用,同时也可以确保数据的封装性,避免对象的内部状态被外部直接访问或修改。

二、角色

2.1 介绍备忘录模式中的三个角色

备忘录模式中有三个核心的角色:

  • 源对象(Originator):保存自身状态的对象,需要在某个时刻保存状态,并且在需要的时候恢复状态。
  • 备忘录(Memento):负责保存源对象的状态,但是不能被源对象直接访问或修改,可以存储源对象中的状态信息,或是一些关键的数据。
  • 负责人(Caretaker):保存和恢复备忘录的状态,充当了源对象与备忘录之间的中介者,可以记录多个备忘录,提供恢复操作。

三、实现方法

3.1 介绍备忘录模式的实现方法

备忘录模式可以有多种实现方法,但是它们都有一个共同的目的,就是保存和恢复对象的状态。备忘录模式可以根据实现方式的不同,分为基于类实现和基于接口实现两种方式。

  1. 基于类实现备忘录模式 基于类实现备忘录模式就是简单地对源对象进行备份,总结起来,它的实现方式包含以下几个步骤:
  • 第一步,定义一个备忘录类,用于保存源对象的状态。
  • 第二步,为备忘录类定义恢复内部状态的方法。
  • 第三步,源对象需要支持保存、恢复状态的操作。
  • 第四步,负责人需要保存多个备忘录对象,提供恢复操作。
  1. 基于接口实现备忘录模式 基于接口实现备忘录模式是通过定义一个接口来实现备忘录模式,它的实现方式包含以下几个步骤:
  • 第一步,定义一个 Memento 接口,用于获取状态、保存状态等方法。
  • 第二步,源对象实现 Memento 接口,并实现接口中的方法。
  • 第三步,负责人需要保存多个备忘录对象,提供恢复操作。

3.2 分别介绍基于类和基于接口实现备忘录模式的方法

基于类的实现方式比基于接口的实现方式更加简单和易用,但是它的灵活性较差。它将源对象的状态存储在一个备忘录类中,源对象需要支持保存、恢复状态的操作,而负责人则需要保存多个备忘录对象,提供恢复操作。

下面是基于类的实现方式的示例代码:

public class Originator {
    private String state;
    public void setState(String state) {
        this.state = state;
    }
    public String getState() {
        return this.state;
    }
    public Memento createMemento() {
        return new Memento(this.state);
    }
    public void restoreMemento(Memento memento) {
        this.state = memento.getState();
    }
    private class Memento {
        private final String state;
        public Memento(String state) {
            this.state = state;
        }
        public String getState() {
            return this.state;
        }
    }
}
public class Caretaker {
    private List<Originator.Memento> states = new ArrayList<>();
    public void addMemento(Originator.Memento memento) {
        states.add(memento);
    }
    public Originator.Memento getMemento(int index) {
        return states.get(index);
    }
}

基于接口的实现方式相对于基于类的方式来说,它更灵活,因为它定义了一个 Memento 接口,这个接口可以被多个源对象实现,提供不同的状态保存和恢复逻辑。下面是基于接口的实现方式的示例代码:

public interface Memento {
    String getState();
}
public class Originator {
    private String state;
    public void setState(String state) {
        this.state = state;
    }
    public String getState() {
        return this.state;
    }
    public void restoreMemento(Memento memento) {
        this.state = memento.getState();
    }
}
public class Caretaker {
    private List<Memento> states = new ArrayList<>();
    public void addMemento(Memento memento) {
        states.add(memento);
    }
    public Memento getMemento(int index) {
        return states.get(index);
    }
}

代码说明:

  1. Memento 接口: 这个接口定义了备忘录的必要方法。在上面的代码中,只有一个 getState() 方法,可以返回需要保存的状态数据。
  2. Originator 类: 这是源对象类,它包含了需要保存的状态数据,并且提供了保存状态和恢复状态的方法。setState() 方法用于设置状态数据,getState() 方法用于获取状态数据。另外,restoreMemento() 方法用于恢复状态,需要传入备忘录对象作为参数,从中获取保存的状态数据。
  3. Caretaker 类: 这是负责人类,它负责保存多个备忘录对象,并在需要的时候进行状态恢复。在上面的代码中,使用了一个 List 来保存备忘录对象,addMemento() 方法用于添加备忘录对象,getMemento() 方法用于获取指定位置的备忘录对象。

四、应用场景

4.1 介绍备忘录模式常见的应用场景

备忘录模式(Memento Pattern)是一种保存对象状态并在需要的时候恢复的设计模式。它可以用于记录和恢复对象状态,同时也可以保护对象的内部状态数据,使其不会被外部直接访问或修改。下面是备忘录模式常见的应用场景:

  1. 撤销和恢复操作 如果你需要实现撤销和恢复功能,备忘录模式可以很好地实现这个需求。当用户进行一些操作后,可以用备忘录模式保存当前状态,并在需要恢复时使用备忘录还原操作。
  2. 数据库事务回滚 备忘录模式可以用于数据库事务回滚,通过保存数据库事务的状态,可以在需要时恢复到之前的状态。备忘录模式还可以用于缓存中,对缓存进行快速的恢复和重试操作,提高系统的性能和稳定性。
  3. 状态保存和恢复 有些高性能系统需要保存运行状态,备忘录模式可以用于保存和恢复对象状态。例如,如果需要实现进程保护,可以使用工作机制,其中进程在工作前会创建快照(备忘录),在需要时可以恢复进程状态。
  4. 对象历史记录 备忘录模式可以用于保存数据、记录和日志,用于实现对象历史记录。例如,可以记录文档编辑器中的撤销和重做操作,将每一步操作保存为一个备忘录,以便在需要时恢复。
  5. 缓存回滚 备忘录模式可以用于实现缓存回滚,缓存回滚功能可以帮助系统在缓存故障或被错误删除时,快速恢复数据。

五、优缺点

5.1 介绍备忘录模式的优点和缺点

优点:

  • 简化了原始对象中的代码:备忘录模式将备份逻辑封装在备忘录类中,使原始对象只需要关注自身状态的管理,有助于简化原始对象中的代码。
  • 提供了撤销操作:备忘录模式可以非常方便地实现撤销操作,因为每次保存状态时,都可以将状态保存到历史记录中,从而允许用户任意撤销到以前的状态。
  • 实现了信息的封装:备忘录保存了原始对象当前状态的信息,但该信息对于外部用户是不可见的,让数据的封装更为有效。
  • 简化了多状态管理:原始对象需要管理多个状态时,使用备忘录模式可以方便地进行多状态的管理,而避免了代码的复杂性。

缺点:

  • 备忘录对象可能占用大量的内存:如果原始对象的状态很大,那么备忘录对象也会变得很大,从而可能占用大量的内存。
  • 无法保护隐私:如果备忘录对象存储了敏感数据,那么备忘录模式就可能无法保护该隐私数据的机密性。
  • 会增加额外的开销:备忘录模式需要在原始对象和备忘录对象之间建立联系,而这种建立联系的过程可能会增加额外的开销。

六、代码实现

6.1 基于接口的备忘录模式代码实现

//备忘录接口
public interface Memento {
    //获取状态
    public String getState();
}
//原始对象
public class Originator {
    private String state;
    public void setState(String state) {
        this.state = state;
    }
    public String getState() {
        return state;
    }
    //保存状态到备忘录
    public Memento saveStateToMemento() {
        return new ConcreteMemento(state);
    }
    //从备忘录中恢复状态
    public void getStateFromMemento(Memento memento) {
        state = memento.getState();
    }
    //具体备忘录类
    private class ConcreteMemento implements Memento {
        private String state;
        public ConcreteMemento(String state) {
            this.state = state;
        }
        public String getState() {
            return state;
        }
    }
}
//备忘录管理类
public class CareTaker {
    private List<Memento> mementoList = new ArrayList<Memento>();
    //添加备忘录
    public void add(Memento state) {
        mementoList.add(state);
    }
    //获取指定备忘录
    public Memento get(int index) {
        return mementoList.get(index);
    }
}

6.2 基于类的备忘录模式代码实现

//备忘录类
public class Memento {
    private String state;
    public Memento(String state) {
        this.state = state;
    }
    public String getState() {
        return state;
    }
}
//原始对象
public class Originator {
    private String state;
    public void setState(String state) {
        this.state = state;
    }
    public String getState() {
        return state;
    }
    //保存状态到备忘录
    public Memento saveStateToMemento() {
        return new Memento(state);
    }
    //从备忘录中恢复状态
    public void getStateFromMemento(Memento memento) {
        state = memento.getState();
    }
}
//备忘录管理类
public class CareTaker {
    private List<Memento> mementoList = new ArrayList<Memento>();
    //添加备忘录
    public void add(Memento state) {
        mementoList.add(state);
    }
    //获取指定备忘录
    public Memento get(int index) {
        return mementoList.get(index);
    }
}

七、与其他模式的比较

7.1 介绍备忘录模式与其他模式的区别和联系

  1. 与命令模式的区别:命令模式可以将操作封装成对象,对一系列操作进行请求、排队和记录;而备忘录模式是对对象状态的保存和恢复,通过备忘录对象实现状态保存和恢复的操作。
  2. 与状态模式的区别:状态模式用于管理有限状态机的行为,根据当前状态调用不同的方法;而备忘录模式则是保存状态数据并用于恢复,备忘录对象用于保存状态数据,可以分离出状态数据的保存和恢复的实现,比较灵活。
  3. 与原型模式的区别:原型模式和备忘录模式都可以用于对象数据的保存和恢复,原型模式以复制现有对象为主,而备忘录模式则是保存状态信息并用于恢复,主要用于记录历史状态的快照。
  4. 与迭代器模式的区别:迭代器模式主要用于遍历集合,备忘录模式则是对对象状态的保存和恢复。

7.2 分别与命令模式、状态模式、原型模式、迭代器模式等比较

  1. 与命令模式的比较:命令模式和备忘录模式都可以对操作进行封装和记录,但是命令模式更关注的是操作本身,而备忘录模式更关注的是对象状态的保存和恢复。
  2. 与状态模式的比较:状态模式和备忘录模式都可以用来保存和恢复状态信息,但是状态模式更关注对象状态的变化和相应的操作,而备忘录模式更关注的是保存状态和恢复状态的实现。
  3. 与原型模式的比较:原型模式和备忘录模式都可以用来保存和恢复状态信息,但是原型模式更关注复制现有对象的功能,而备忘录模式更关注历史状态的快照和恢复的实现。
  4. 与迭代器模式的比较:迭代器模式和备忘录模式都可以用来操作集合,但是迭代器模式更关注对集合进行顺序遍历,而备忘录模式更关注保存状态和恢复状态。

八、总结

8.1 总结备忘录模式的主要内容

备忘录模式是一种行为型设计模式,它的主要作用是在不破坏封装性的前提下,保存和恢复对象的状态。通过备忘录模式,可以让对象在多种状态之间进行切换,同时也可以方便地实现撤销和重做操作。

备忘录模式通常包含以下几个角色:

  1. Originator(原始对象):具有保存状态和恢复状态的功能。
  2. Memento(备忘录):负责存储原始对象的状态。
  3. CareTaker(备忘录管理类):负责管理备忘录,提供保存和获取备忘录的接口。

备忘录模式主要解决了如何在不破坏封装性的前提下保存和恢复对象的状态,让对象在不同状态之间进行切换,从而满足一些业务需求。

8.2 核心思想、应用场景、代码实现、优缺点等

  1. 核心思想 备忘录模式的核心思想是通过备忘录对象存储原始对象当前状态的信息,现场决定保存或不保存原始对象状态,当需要恢复对象状态时,通过备忘录对象将原始对象状态进行还原。
  2. 应用场景 备忘录模式通常适用于以下场景:
  • 当需要保存多个状态时,可以使用备忘录模式快速实现多状态的管理,避免了代码的复杂性。
  • 当需要提供撤销操作时,可以使用备忘录模式非常方便地实现撤销操作,因为每次保存状态时,都可以将状态保存到历史记录中,从而允许用户任意撤销到以前的状态。
  1. 代码实现 备忘录模式的代码实现通常包括备忘录接口、原始对象、备忘录管理类等角色。

具体的实现方式可以基于接口或者基于类实现。

  1. 优缺点 备忘录模式的优点包括:
  • 简化了原始对象中的代码
  • 提供了撤销操作
  • 实现了信息的封装
  • 简化了多状态管理
  1. 备忘录模式的缺点包括:
  • 备忘录对象可能占用大量的内存
  • 无法保护隐私
  • 会增加额外的开销

因此,在使用备忘录模式时,需要权衡各种因素,并结合具体应用场景进行选择。

九、总评

9.1 总结备忘录模式的主要优势、注意事项和使用场景,以及如何更好地应用备忘录模式。

备忘录模式是一种行为型设计模式,它允许在不破坏对象封装性的前提下,将对象的内部状态保存在外部,并在需要时恢复这些状态。备忘录模式的主要优势在于:

  1. 对象的状态可以被保存和恢复,可以支持撤销和回滚操作。
  2. 建立了一种良好的封装方式,使得对象的状态存储和获取与对象本身相分离。
  3. 灵活性高,可以支持多种状态存储和恢复的策略。

注意事项如下:

  1. 备忘录对象可能会占用大量的内存,需要谨慎使用,尤其是对于大型对象和状态的情况。
  2. 由于备忘录模式会产生多个对象状态的备份,因此可能会影响系统的性能和效率。

备忘录模式通常适用于以下场景:

  1. 当需要保存一个对象在某个时刻的状态或部分状态时,可以使用备忘录模式。
  2. 当需要通过撤销或回滚操作来恢复对象的状态时,可以使用备忘录模式。
  3. 当需要防止因为内部状态暴露导致的对象封装性被破坏时,可以使用备忘录模式。

使用备忘录模式需要注意以下几点:

  1. 需要考虑备忘录对象的存储和获取方式,以及不同对象状态的存储和恢复策略。
  2. 备忘录对象应该尽可能地与被保存状态的对象分离,以达到良好的封装性。
  3. 备忘录模式可以和其他设计模式组合使用,比如命令模式、观察者模式等,以实现更复杂的功能。

要更好地应用备忘录模式,需要遵循以下几个实践建议:

  1. 尽量使用接口或抽象类定义备忘录对象和管理器,可增加代码的可维护性和扩展性。
  2. 及时清理备忘录对象,以避免内存泄漏。
  3. 在使用备忘录模式时,要充分考虑系统的性能和效率,以确保系统的稳定性和响应速度。

综上所述,备忘录模式是一种实现对象状态备份和恢复的有效方式。通过应用备忘录模式,可以增强系统的可维护性和扩展性,并防止对象因状态泄漏等原因导致封装性被破坏的问题。

目录
相关文章
|
7月前
|
设计模式 存储 Java
23种设计模式,备忘录模式的概念优缺点以及JAVA代码举例
【4月更文挑战第9天】备忘录模式是一种行为设计模式,它能在不破坏对象封装的前提下,捕获并保存对象的当前状态,以便后面可以恢复到这个状态。
87 0
|
1月前
|
Java API Apache
Java编程如何读取Word文档里的Excel表格,并在保存文本内容时保留表格的样式?
【10月更文挑战第29天】Java编程如何读取Word文档里的Excel表格,并在保存文本内容时保留表格的样式?
123 5
|
3月前
|
存储 设计模式 安全
Java设计模式-备忘录模式(23)
Java设计模式-备忘录模式(23)
|
4月前
|
存储 JSON 前端开发
【Java】用@JsonFormat(pattern = “yyyy-MM-dd“)注解,出生日期竟然年轻了一天
在实际项目中,使用 `@JsonFormat(pattern = &quot;yyyy-MM-dd&quot;)` 注解导致出生日期少了一天的问题,根源在于夏令时的影响。本文详细解析了夏令时的概念、`@JsonFormat` 注解的使用方法,并提供了三种解决方案:在注解中添加 `timezone = GMT+8`、修改 JVM 参数 `-Duser.timezone=GMT+08`,以及使用 `timezone = Asia/Shanghai
446 0
【Java】用@JsonFormat(pattern = “yyyy-MM-dd“)注解,出生日期竟然年轻了一天
|
5月前
|
Java
Java中将保留四位小数的Double转换为String的方法详解
选择合适的方法,可以使代码更加简洁、高效,同时也能满足不同场景下的需求。
87 5
|
6月前
|
设计模式 存储 Java
Java设计模式之备忘录模式详解
Java设计模式之备忘录模式详解
|
7月前
|
设计模式 安全 Java
【设计模式】JAVA Design Patterns——Curiously Recurring Template Pattern(奇异递归模板模式)
该文介绍了一种C++的编程技巧——奇异递归模板模式(CRTP),旨在让派生组件能继承基本组件的特定功能。通过示例展示了如何创建一个`Fighter`接口和`MmaFighter`类,其中`MmaFighter`及其子类如`MmaBantamweightFighter`和`MmaHeavyweightFighter`强制类型安全,确保相同重量级的拳手之间才能进行比赛。这种设计避免了不同重量级拳手间的错误匹配,编译时会报错。CRTP适用于处理类型冲突、参数化类方法和限制方法只对相同类型实例生效的情况。
【设计模式】JAVA Design Patterns——Curiously Recurring Template Pattern(奇异递归模板模式)
|
6月前
|
Java
Java的double值保留2位小数
【6月更文挑战第16天】Java的double值保留2位小数
244 0
|
7月前
|
设计模式 存储 Java
Java设计模式:解释一下单例模式(Singleton Pattern)。
`Singleton Pattern`是Java中的创建型设计模式,确保类只有一个实例并提供全局访问点。它通过私有化构造函数,用静态方法返回唯一的实例。类内静态变量存储此实例,对外仅通过静态方法访问。
53 1
|
7月前
|
Java
Java PDF 相关 1、拷贝多个PDF到一个PDF,并且文件大小变小,文本等信息保留
1、合并多个PDF,并且文件变小,后面添加的文本信息保留
158 0