设计模式-备忘录模式

1、概述

备忘录模式(Memento Pattern)是一种行为型设计模式,它允许你捕获对象的内部状态,并在需要时恢复该状态,而无需暴露该对象的实现细节。所谓备忘录模式就是在不破坏封装的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样,以后就可以将该对象恢复到原先保存的状态。

发起人(Originator)

角色定义:记录当前时刻的内部状态,并可以恢复到一个先前状态(即它之前保存至备忘录的状态)。
主要责任:
创建一个备忘录对象,用于记录当前时刻的内部状态。
使用备忘录对象来恢复内部状态。
关注点:关注内部状态的保存与恢复,但不需要关心备忘录对象的具体存储和管理。

管理者(Caretaker)

角色定义:负责保存备忘录对象,并且在需要的时候提供备忘录对象给发起人。
主要责任:
在发起人请求时提供备忘录对象给发起人保存其当前状态。
在发起人需要时,提供之前保存的备忘录对象以恢复状态。
关注点:关注备忘录对象的存储和提供,但不关心备忘录对象的具体内容。

备忘录(Memento)

角色定义:负责存储发起人对象的内部状态,并且可以防止发起人以外的其他对象访问备忘录的内容。
主要责任:
存储发起人对象的内部状态。
提供一种方式来恢复发起人对象的内部状态。
关注点:关注状态的存储和恢复,同时保护这些状态不被未授权的对象访问。

2、优缺点

主要作用

备忘录模式的主要作用是在不破坏对象封装性的前提下,保存对象的某个状态,以便在未来某个时刻恢复该对象到此状态。

优点

状态恢复能力:备忘录模式允许对象在不破坏封装性的前提下保存其内部状态,并在需要时恢复到这些状态。这种能力特别适用于那些需要频繁回退或撤销操作的应用场景,比如文本编辑器、游戏等。通过保存和恢复状态,用户可以轻松地撤销之前的操作,提高了用户体验。
封装性保护:备忘录模式通过引入备忘录对象来保存状态信息,而不需要暴露发起人的内部细节。这使得发起人对象可以保持其封装性,不会被外部对象直接访问和修改其内部状态。这有助于保护对象的状态安全,防止意外修改或破坏。
降低耦合度:在备忘录模式中,发起人对象和管理者对象之间的耦合度较低。发起人对象只需要提供创建备忘录和恢复状态的方法,而不需要关心备忘录的具体存储和管理。管理者对象负责保存和提供备忘录对象,但不需要了解发起人对象的内部实现。这种松耦合的设计使得代码更加灵活和可维护。
支持多次恢复:通过保存多个不同时刻的备忘录对象,备忘录模式支持对象恢复到不同的历史状态。这使得应用可以更加灵活地处理用户操作,提供更加丰富的功能和体验。

缺点

资源消耗:当发起人角色的状态需要完整地存储到备忘录对象中时,如果状态信息复杂或庞大,将会占用大量的存储空间。这可能导致在资源有限的环境中,如移动设备或嵌入式系统,出现性能问题或存储限制。
管理复杂性:备忘录模式引入了一个新的角色——管理者,用于保存和提供备忘录对象。这增加了系统的复杂性,特别是在大型系统中,需要额外管理备忘录对象的存储、检索和过期等问题。
状态同步问题:如果发起人对象的状态在多个地方被修改,而备忘录对象没有及时更新,那么恢复状态可能会导致数据不一致或冲突。因此,需要确保备忘录对象的更新与发起人对象的状态变化保持同步,这增加了开发的复杂性和潜在的错误风险。
撤销操作的局限性:备忘录模式主要关注状态的保存和恢复,但对于复杂的撤销操作(如跨多个对象的撤销或条件性撤销)可能不够灵活。在某些情况下,可能需要结合其他设计模式或机制来实现更高级的撤销功能。

3、实现方式

import java.io.Serializable;
import java.util.Deque;
import java.util.LinkedList;


public class Test {
    public static void main(String[] args) {
        WebBrowser browser = new WebBrowser("https://www.baidu.com");

        // 模拟用户访问几个页面
        browser.loadUrl("https://www.qq.com");
        browser.loadUrl("https://www.sina.com.cn");

        // 模拟用户点击后退按钮
        browser.goBack();

        // 显示当前页面的URL,应该是用户之前访问的页面
        System.out.println("Current URL after going back: " + browser.getCurrentUrl());
        // 应该输出 "https://www.google.com"
    }
}

interface Memento {
    String getUrl();
    // 可以添加其他状态信息,比如页面标题、滚动位置等
}


class BrowserHistoryMemento implements Memento, Serializable {
    private String url;

    public BrowserHistoryMemento(String url) {
        this.url = url;
    }

    @Override
    public String getUrl() {
        return url;
    }

    // 省略其他可能的状态信息
}

interface Originator {
    Memento createMemento();

    void setMemento(Memento memento);
    // 其他浏览器功能的方法,如加载页面等
}


class WebBrowser implements Originator {
    private String currentUrl;
    private Deque<Memento> historyStack = new LinkedList<>();

    public WebBrowser(String initialUrl) {
        this.currentUrl = initialUrl;
    }

    @Override
    public Memento createMemento() {
        return new BrowserHistoryMemento(currentUrl);
    }

    @Override
    public void setMemento(Memento memento) {
        if (memento != null && memento instanceof BrowserHistoryMemento) {
            BrowserHistoryMemento browserHistoryMemento = (BrowserHistoryMemento) memento;
            this.currentUrl = browserHistoryMemento.getUrl();
        }
    }

    public void loadUrl(String url) {
        this.currentUrl = url;
        // 在访问新页面之前,先将当前页面的状态压入历史栈
        historyStack.push(createMemento());
    }

    public void goBack() {
        if (!historyStack.isEmpty()) {
            // 从历史栈中弹出最后一个状态,并获取上一个值
            historyStack.pop();
            setMemento(historyStack.getLast());
        }
    }

    // 获取当前URL的方法,用于显示或其他操作
    public String getCurrentUrl() {
        return currentUrl;
    }
}

4、应用场景

主要包括以下几个方面

撤销操作:在需要支持撤销功能的系统中,如文本编辑、绘图工具等,使用备忘录模式可以方便地保存历史状态,实现撤销功能。
状态恢复:对于需要记录并恢复对象历史状态的场景,如游戏存档、业务流程管理等,备忘录模式能够确保状态的完整性和可恢复性。
资源管理:在某些需要管理资源状态的场景中,可以使用备忘录模式来保存和恢复资源状态。例如,在操作系统中,资源管理器可能需要保存当前资源分配状态,以便在需要时能够恢复到之前的状态,确保系统的稳定性和可靠性。
事务处理:在数据库或分布式系统中,利用备忘录模式可以在事务失败时回滚到之前的状态,保证数据的一致性和完整性。

实际应用

文本编辑器中的撤销功能:在编写文档时,用户经常需要撤销之前的输入或删除操作。通过备忘录模式,编辑器可以保存用户的每一步操作,当用户选择撤销时,可以恢复到之前的状态。
游戏中的存档和读档功能:玩家在进行游戏时,可能会希望随时保存当前的游戏进度,以便在之后能够继续游戏。通过备忘录模式,游戏可以保存玩家的状态和游戏进度,并在玩家需要时恢复这些状态。
浏览器中的后退功能:当用户在浏览网页时,可能会通过点击链接跳转到其他页面。浏览器使用备忘录模式来保存用户的浏览历史,当用户点击后退按钮时,可以恢复到之前浏览的页面。
数据库事务处理:在进行数据库操作时,一系列操作可能需要作为一个整体来执行。如果其中某个操作失败,整个事务都应该回滚到操作之前的状态。通过使用备忘录模式,可以保存事务开始时的状态,并在需要时回滚到该状态。
软件中的“撤销”操作:许多软件都提供了撤销功能,允许用户撤销之前的操作。这些软件使用备忘录模式来记录用户的每一步操作,以便在用户选择撤销时能够恢复到之前的状态。

This entry was posted in 应用, 设计模式. Bookmark the permalink.