【再谈设计模式】备忘录模式~对象状态的守护者

本文涉及的产品
实时计算 Flink 版,1000CU*H 3个月
智能开放搜索 OpenSearch行业算法版,1GB 20LCU 1个月
实时数仓Hologres,5000CU*H 100GB 3个月
简介: 备忘录模式属于行为型设计模式。它的主要目的是在不破坏对象封装性的前提下,捕获并外部化一个对象的内部状态,以便之后可以将该对象恢复到这个状态。原发器(Originator):创建一个备忘录,用于记录当前时刻它的内部状态。原发器还可以使用备忘录来恢复其内部状态。备忘录(Memento):存储原发器对象的内部状态。备忘录应该防止原发器以外的其他对象访问其内部状态。负责人(Caretaker):负责保存备忘录,但不能对备忘录的内容进行操作或检查。

{71F90506-A69C-48D3-A0AE-812756426E38}.png

一、引言

在软件工程,软件开发过程中,我们常常会遇到需要保存对象状态以便在之后恢复的情况。例如,在文本编辑器中,我们可能想要撤销之前的操作;在游戏中,玩家可能希望恢复到之前的某个游戏状态。备忘录模式(Memento Pattern)就为这种需求提供了一种有效的解决方案。

image.png

二、定义与描述

备忘录模式属于行为型设计模式。它的主要目的是在不破坏对象封装性的前提下,捕获并外部化一个对象的内部状态,以便之后可以将该对象恢复到这个状态。这个模式涉及到三个主要角色:

原发器(Originator):创建一个备忘录,用于记录当前时刻它的内部状态。原发器还可以使用备忘录来恢复其内部状态。

备忘录(Memento):存储原发器对象的内部状态。备忘录应该防止原发器以外的其他对象访问其内部状态。

负责人(Caretaker):负责保存备忘录,但不能对备忘录的内容进行操作或检查。

{39F2AE09-01F5-4276-8792-1364B502ECDF}.png

三、抽象背景

在许多应用场景中,对象的状态会随着时间发生变化。然而,我们可能需要在某个特定的时间点保存对象的状态,以便后续可以撤销操作或者回到之前的某个状态。如果直接将对象的状态暴露给外部进行保存和恢复,会破坏对象的封装性。备忘录模式通过引入一个专门的备忘录对象来解决这个问题,使得对象的状态可以在不破坏封装性的情况下被保存和恢复。

image.png

四、适用场景与现实问题解决

(一)适用场景

文本编辑器的撤销/重做功能:当用户输入文字、删除文字或者进行格式调整时,每一个操作都可以看作是对象(文本内容)状态的改变。通过备忘录模式,可以轻松地保存每个操作前的文本状态,从而实现撤销和重做功能。

{77AC3A8B-CF8A-4B3B-ADAB-69FAF89C7073}.png

游戏中的存档和读档功能:游戏中的各种元素(角色属性、游戏场景等)构成了对象的状态。玩家在游戏过程中可能希望在某些关键节点保存游戏状态,之后可以随时读取之前保存的状态继续游戏。

{8E1D8BB8-29F7-46A0-8198-F9986E543592}.png

(二)现实问题解决

以文本编辑器为例,假设我们有一个TextDocument    类作为原发器。在用户进行编辑操作(如插入字符、删除字符等)之前,我们可以创建一个备忘录来保存当前文档的状态。如果用户执行了撤销操作,我们可以从备忘录中恢复文档的之前状态。这样就可以在不破坏TextDocument类内部结构的情况下,实现撤销功能。

五、备忘录模式的现实生活的例子

考虑拍照的过程。相机可以看作是原发器,照片就是备忘录。当我们按下快门(相当于创建备忘录)时,相机的当前状态(镜头聚焦、光圈大小、曝光设置等)被记录在照片上。我们可以将照片存储起来(相当于负责人保存备忘录),之后如果想要回顾某个瞬间(相当于恢复到之前的状态),我们可以查看对应的照片。

image.png

六、初衷与问题解决

初衷是在保持对象封装性的同时,实现对象状态的保存和恢复。通过将对象状态的保存和恢复逻辑封装在备忘录模式中,避免了外部对象直接操作对象的内部状态,从而提高了代码的可维护性和安全性。

七、代码示例

(一)Java

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

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

    public String getState() {
   
        return state;
    }
}

// 原发器类
class Originator {
   
    private String state;

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

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

    public void restoreFromMemento(Memento memento) {
   
        state = memento.getState();
    }
}

// 负责人类
class Caretaker {
   
    private Memento memento;

    public void saveMemento(Memento memento) {
   
        this.memento = memento;
    }

    public Memento getMemento() {
   
        return memento;
    }
}

public class Main {
   
    public static void main(String[] args) {
   
        Originator originator = new Originator();
        originator.setState("State 1");
        Caretaker caretaker = new Caretaker();
        caretaker.saveMemento(originator.saveToMemento());
        originator.setState("State 2");
        originator.restoreFromMemento(caretaker.getMemento());
        System.out.println(originator.state);
    }
}

类图:
{198CD646-161D-4776-B9E4-2ED2AEBECAA0}.png

流程图:
{C913191F-51E4-460E-BBA5-039DC6248BFE}.png

时序图:
{DB6EC6CF-F510-41D5-ABE2-493F46758C5B}.png

(二)C++

#include <iostream>
#include <string>

// 备忘录类
class Memento {
   
private:
    std::string state;
public:
    Memento(std::string state) : state(state) {
   }
    std::string getState() const {
    return state; }
};

// 原发器类
class Originator {
   
private:
    std::string state;
public:
    void setState(std::string state) {
    this->state = state; }
    Memento saveToMemento() {
    return Memento(state); }
    void restoreFromMemento(const Memento& memento) {
    state = memento.getState(); }
};

// 负责人类
class Caretaker {
   
private:
    Memento memento;
public:
    void saveMemento(const Memento& memento) {
    this->memento = memento; }
    Memento getMemento() const {
    return memento; }
};

int main() {
   
    Originator originator;
    originator.setState("State 1");
    Caretaker caretaker;
    caretaker.saveMemento(originator.saveToMemento());
    originator.setState("State 2");
    originator.restoreFromMemento(caretaker.getMemento());
    std::cout << originator.state << std::endl;
    return 0;
}

(三)Python

# 备忘录类
class Memento:
    def __init__(self, state):
        self.state = state

    def get_state(self):
        return self.state


# 原发器类
class Originator:
    def __init__(self):
        self.state = None

    def set_state(self, state):
        self.state = state

    def save_to_memento(self):
        return Memento(self.state)

    def restore_from_memento(self, memento):
        self.state = memento.get_state()


# 负责人类
class Caretaker:
    def __init__(self):
        self.memento = None

    def save_memento(self, memento):
        self.memento = memento

    def get_memento(self):
        return self.memento


if __name__ == "__main__":
    originator = Originator()
    originator.set_state("State 1")
    caretaker = Caretaker()
    caretaker.save_memento(originator.save_to_memento())
    originator.set_state("State 2")
    originator.restore_from_memento(caretaker.get_memento())
    print(originator.state)

(四)Go

package main

import "fmt"

// 备忘录结构体
type Memento struct {
   
    state string
}

func NewMemento(state string) *Memento {
   
    return &Memento{
   state: state}
}

func (m *Memento) getState() string {
   
    return m.state
}

// 原发器结构体
type Originator struct {
   
    state string
}

func (o *Originator) setState(state string) {
   
    o.state = state
}

func (o *Originator) saveToMemento() *Memento {
   
    return NewMemento(o.state)
}

func (o *Originator) restoreFromMemento(m *Memento) {
   
    o.state = m.getState()
}

// 负责人结构体
type Caretaker struct {
   
    memento *Memento
}

func (c *Caretaker) saveMemento(m *Memento) {
   
    c.memento = m
}

func (c *Caretaker) getMemento() *Memento {
   
    return c.memento
}

func main() {
   
    originator := Originator{
   }
    originator.setState("State 1")
    caretaker := Caretaker{
   }
    caretaker.saveMemento(originator.saveToMemento())
    originator.setState("State 2")
    originator.restoreFromMemento(caretaker.getMemento())
    fmt.Println(originator.state)
}

八、备忘录模式的优缺点

(一)优点

保持封装性:原发器的内部状态对外部是隐藏的,只有原发器自身能够访问备忘录中的状态,从而保护了对象的封装性。

简化撤销/恢复操作:通过备忘录模式,撤销和恢复操作可以很容易地实现,不需要在原发器中编写复杂的状态管理代码。

提供状态历史记录:可以方便地保存多个备忘录,从而形成对象状态的历史记录,这对于需要查看对象状态变化历史的应用场景非常有用。

(二)缺点

资源消耗:如果需要保存大量的备忘录,可能会消耗较多的内存资源,尤其是当备忘录对象包含大量数据时。

管理复杂度:随着备忘录数量的增加,备忘录的管理(如存储、查找、删除等)可能会变得复杂。

{FD993414-CA3E-46D5-9A49-846CF462EDCA}.png

九、备忘录模式的升级版

一种常见的升级版是增加一个历史列表(History List)管理类,它可以管理多个备忘录对象,并且提供更方便的操作,如按照时间顺序查看备忘录、限制备忘录的数量以避免资源过度消耗等。例如,在文本编辑器的撤销/重做功能中,这个历史列表可以存储多个编辑操作的备忘录,并且可以方便地根据用户的操作(如多次撤销或重做)找到对应的备忘录进行状态恢复。

{9A3E8018-27F7-4ED5-809A-26ED2A0F421B}.png

目录
相关文章
|
4月前
|
设计模式 存储 Java
【设计模式】【行为型模式】备忘录模式(Memento)
一、入门 什么是备忘录模式? 备忘录模式(Memento Pattern)是一种行为设计模式,用于在不破坏封装性的前提下,捕获并外部化一个对象的内部状态,以便在需要时恢复该状态。它通常用于实现撤销操作
154 8
|
7月前
|
设计模式 供应链 安全
【再谈设计模式】中介者模式 - 协调对象间交互的枢纽
中介者模式定义了一个中介对象来封装一组对象之间的交互方式。中介者使得各对象之间不需要显式地相互引用,从而降低了它们之间的耦合度。它通过将对象之间的交互逻辑集中到中介者对象中,使得系统的结构更加清晰,易于维护和扩展。
147 18
【再谈设计模式】中介者模式 - 协调对象间交互的枢纽
|
7月前
|
设计模式 Java Go
【再谈设计模式】状态模式~对象行为的状态驱动者
状态模式属于行为型设计模式。它将对象的行为封装在不同的状态类中,使得对象在不同的状态下表现出不同的行为。上下文(Context):这是一个包含状态对象的类,它定义了客户感兴趣的接口,并维护一个具体状态对象的引用。上下文将操作委托给当前的状态对象来处理。抽象状态(State):这是一个抽象类或者接口,它定义了一个特定状态下的行为接口。所有具体的状态类都实现这个接口。具体状态(Concrete State):这些是实现抽象状态接口的类,每个具体状态类实现了与该状态相关的行为。
183 18
|
7月前
|
设计模式 算法 Java
【再谈设计模式】访问者模式~操作对象结构的新视角
  访问者模式是一种行为设计模式,旨在解决对象结构与操作逻辑的耦合问题。在软件系统开发中,当面临复杂的对象结构(如多种类型对象组成的树形或图形结构),且需要对这些对象执行不同操作时,传统方式将操作直接写在对象类中会导致类职责过多,不利于维护和扩展。而访问者模式通过将操作与对象结构分离,允许在不改变现有对象结构的情况下定义新操作,元素接受访问者访问,访问者定义对不同类型元素的操作逻辑,从而为应对这种复杂情况提供了有效的解决方案。
103 0
|
12月前
|
存储 设计模式 安全
Java设计模式-备忘录模式(23)
Java设计模式-备忘录模式(23)
103 3
|
设计模式 缓存 Java
Java设计模式:享元模式实现高效对象共享与内存优化(十一)
Java设计模式:享元模式实现高效对象共享与内存优化(十一)
|
设计模式 算法
设计模式的基础问题之备忘录模式在软件开发的问题如何解决
设计模式的基础问题之备忘录模式在软件开发的问题如何解决
|
设计模式
备忘录模式-大话设计模式
备忘录模式-大话设计模式
|
设计模式 存储 安全
18 Java反射reflect(类加载+获取类对象+通用操作+设计模式+枚举+注解)
18 Java反射reflect(类加载+获取类对象+通用操作+设计模式+枚举+注解)
284 0
|
设计模式 数据处理
iLogtail设计模式问题之什么是备忘录模式
iLogtail设计模式问题之什么是备忘录模式