设计模式(行为型)-备忘录模式
目录
定义
类图
角色
角色详解
(一)发起人角色(Originator)
(二)备忘录角色(Memento)
(三)备忘录管理员角色(Caretaker)
优缺点
优点
缺点
使用场景
可回滚的操作场景
游戏存档场景
需要监控的副本场景
定义
备忘录模式,英文名为 Memento Pattern,是 GoF(Gang of Four,即设计模式领域的四位大师:Erich Gamma、Richard Helm、Ralph Johnson 和 John Vlissides)提出的 23 种经典设计模式之一,属于行为型设计模式范畴。其核心定义为:在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。如此一来,后续便能够将该对象恢复到先前保存的状态。
打个比方,我们在使用绘图软件时,可能会进行一系列的绘图操作,如绘制图形、填充颜色、调整线条粗细等。如果没有备忘录模式,一旦我们操作失误,比如不小心删除了重要图形或者对颜色填充不满意,可能就很难恢复到之前理想的状态。但有了备忘录模式,绘图软件就可以在每次关键操作前,将当前画布的状态(即对象的内部状态,包括绘制的图形、颜色设置、线条样式等)捕获并保存起来。当我们想要撤销操作时,软件就能从这些保存的状态中取出对应的状态,将画布恢复到之前的样子。
类图
角色
备忘录模式的类图主要涉及三个核心角色,它们相互协作,共同实现了对象状态的保存与恢复功能。
-
发起人角色(Originator):这一角色在类图中处于核心地位,它就像是一个拥有自主记忆能力的个体。发起人角色负责记录当前时刻自身的内部状态,同时具备创建和恢复备忘录数据的关键能力。在代码实现层面,它通常会有一个方法用于创建备忘录,将自身当前的状态信息打包存储到备忘录对象中;另外还会有一个方法,当需要恢复状态时,从传入的备忘录对象中提取信息,恢复自身的状态。例如,在一个游戏角色类中,游戏角色就是发起人角色,它可以创建一个备忘录来记录自己当前的等级、生命值、装备等状态,也能够从一个已有的备忘录中读取这些信息,恢复到之前保存的状态。
-
备忘录管理员角色(Caretaker):可以把备忘录管理员角色看作是一个细心的图书管理员,它负责对备忘录进行管理,包括保存和提供备忘录。然而,它并不直接操作备忘录中的内容,仅仅起到一个中间协调和管理的作用。在类图中,它与备忘录角色和发起人角色都有一定的关联,它从发起人角色那里获取备忘录对象并妥善保存,当发起人角色需要恢复状态时,再将对应的备忘录对象提供给发起人角色。比如在游戏存档系统中,备忘录管理员角色可能会将游戏角色创建的备忘录(记录了游戏角色的各种状态)存储到硬盘的特定位置,当玩家需要读取存档恢复游戏角色状态时,它再从存储位置取出对应的备忘录提供给游戏角色(发起人角色)。
-
备忘录角色(Memento):备忘录角色如同一个精心设计的容器,专门用于存储发起人角色的内部状态。为了确保封装性,它通常会对状态信息进行合理的封装,防止外部对象随意访问和修改。在类图中,备忘录角色与发起人角色紧密关联,发起人角色能够访问备忘录角色中存储的详细状态信息,而其他外部角色则只能看到备忘录角色提供的有限接口,无法直接窥探和篡改其中的状态数据。以绘图软件为例,备忘录角色可能会存储画布上所有图形的坐标、形状、颜色等详细信息,这些信息对于绘图软件的核心逻辑(发起人角色)来说是可访问的,以便在需要时进行状态恢复,但对于外部的普通用户操作接口来说,是不可见且不可随意修改的。
角色详解
(一)发起人角色(Originator)
-
状态记录:发起人角色需要精确地记录自身当前时刻的内部状态。这要求它对自身的各个属性和状态有清晰的认知,并能够将这些信息以一种可存储的方式整理出来。例如,在一个财务记账系统中,记账本对象作为发起人角色,需要记录当前的账目余额、每一笔交易的明细(包括交易时间、金额、对方账户等)等内部状态信息。
-
备忘录创建:发起人角色拥有创建备忘录的能力,它将当前记录好的内部状态信息封装到一个备忘录对象中。这个过程就像是把一本书的所有内容整理好后放进一个文件袋里。在代码实现上,通常会创建一个备忘录类的实例,并将自身的状态信息赋值给备忘录类的相应属性。比如,在一个文本编辑器中,文本对象作为发起人角色,当需要创建备忘录时,它会将当前文本的内容、光标位置、字体设置等信息封装到一个备忘录对象中。
-
状态恢复:当需要恢复到之前保存的状态时,发起人角色能够从传入的备忘录对象中提取相应的状态信息,并将自身的状态恢复到备忘录所记录的状态。这就如同从文件袋中取出书,重新阅读并按照书中记录的内容调整自身状态。例如,在一个图形设计软件中,图形对象作为发起人角色,当接收到一个包含之前图形状态信息的备忘录对象时,它会读取备忘录中的图形形状、颜色、位置等信息,将自身的图形状态恢复到对应的样子。
(二)备忘录角色(Memento)
-
状态存储:备忘录角色的主要职责就是安全、可靠地存储发起人角色的内部状态。它会根据发起人角色的状态信息结构,设计相应的属性来存储这些信息。例如,在一个游戏角色的备忘录中,会有属性来存储角色的等级、生命值、魔法值、背包物品清单等信息。这些属性通常会被设置为私有的,以保证状态信息的封装性,防止外部非法访问和修改。
-
访问控制:为了维护封装性,备忘录角色会对状态信息的访问进行严格控制。对于发起人角色,它提供足够的接口,使得发起人能够读取和设置状态信息,以便进行状态恢复操作。但对于其他外部角色,只提供非常有限的接口,甚至不提供任何接口,避免外部对象随意获取和篡改备忘录中的状态信息。比如,在一个数据库事务的备忘录中,只有数据库事务处理模块(发起人角色)能够直接访问备忘录中记录的事务状态、数据修改内容等信息,而其他无关的业务模块则无法直接访问这些敏感信息。
(三)备忘录管理员角色(Caretaker)
-
备忘录保存:备忘录管理员角色负责接收发起人角色创建的备忘录对象,并将其妥善保存起来。保存的方式可以有多种,比如存储在内存中的数据结构里(如列表、栈等),或者存储到外部存储设备(如硬盘、数据库)中。在一个文档编辑软件中,当用户进行保存操作时,文档对象(发起人角色)创建一个备忘录记录当前文档的状态,备忘录管理员角色则将这个备忘录存储到硬盘上的特定文档版本管理目录中。
-
备忘录提供:当发起人角色需要恢复状态时,备忘录管理员角色能够准确地找到并提供相应的备忘录对象给发起人角色。这就要求备忘录管理员角色在保存备忘录时,有合理的索引和管理机制,以便能够快速定位到需要的备忘录。例如,在一个版本控制系统中,备忘录管理员角色会根据版本号等信息,将对应的备忘录提供给需要恢复到特定版本状态的文件对象(发起人角色)。
优缺点
优点
-
状态恢复机制:备忘录模式为用户提供了一种极为便捷的状态恢复机制。在许多应用场景中,用户可能会因为误操作或者想要尝试不同的操作路径,而需要回到之前的某个状态。例如在文档编辑过程中,用户可能不小心删除了重要段落,通过备忘录模式实现的撤销功能,就可以轻松恢复到删除前的状态;在图形设计中,对某个图形的操作不满意,也能利用备忘录模式回到操作前的状态。这种状态恢复机制极大地提高了用户体验,减少了用户因为错误操作而产生的焦虑和时间浪费。
-
信息封装性:通过将对象的内部状态封装在备忘录对象中,备忘录模式实现了良好的信息封装。外部对象无法直接访问和修改发起人对象的内部状态,只有发起人对象自身能够通过备忘录对象来管理和恢复自己的状态。这不仅提高了系统的安全性,防止了外部非法操作对对象内部状态的破坏,还使得系统的结构更加清晰,各模块之间的耦合度降低。例如,在一个企业资源规划(ERP)系统中,财务模块的内部状态(如账目余额、成本核算数据等)通过备忘录模式进行封装,其他业务模块无法直接访问和修改这些敏感信息,保证了财务数据的准确性和安全性。
-
简化发起人类:备忘录模式减轻了发起人类的负担,使其不再需要自行管理和保存自身内部状态的多个版本。在没有使用备忘录模式时,发起人类可能需要维护复杂的数据结构来记录自身的状态变化历史,这会增加代码的复杂性和维护成本。而采用备忘录模式后,发起人只需要专注于自身的核心业务逻辑,状态的保存和管理工作由备忘录角色和备忘录管理员角色来完成。比如,在一个游戏角色类中,原本可能需要在游戏角色类内部维护一个复杂的状态栈来记录每次升级、战斗后的状态变化,使用备忘录模式后,游戏角色类只需要在需要时创建和恢复备忘录,状态的存储和管理工作交给了备忘录和备忘录管理员。
缺点
-
资源消耗问题:如果发起人角色的状态数据量较大,并且需要频繁地创建和保存备忘录,那么在资源消耗方面会面临较大压力。每个备忘录对象都需要占用一定的内存空间,如果长时间积累大量的备忘录,可能会导致内存不足等问题。例如,在一个大型 3D 建模软件中,模型对象的状态(包括复杂的几何形状、材质纹理、光照设置等)数据量巨大,每保存一次状态就会生成一个庞大的备忘录对象,如果用户频繁进行保存操作,会迅速消耗大量的内存资源。
-
存储容量不确定性:备忘录管理员角色在保存备忘录时,往往难以预先知道一个备忘录对象会占用多大的存储空间。这就可能导致在存储管理方面出现问题,比如在有限的硬盘空间中,可能因为不断保存备忘录而导致空间不足,但又无法提前提醒用户某个操作会产生较大的存储开销。例如,在一个手机游戏中,游戏存档(即备忘录)的大小可能会因为玩家在游戏中的不同行为(如收集大量物品、解锁复杂剧情等)而有很大差异,游戏开发者很难准确预估每个存档的大小,这可能会给玩家带来存储空间不足的困扰。
-
状态有效性问题:当发起人角色的状态发生改变时,新的状态并不一定总是有效的。在这种情况下,如果频繁使用备忘录模式进行状态恢复,可能会导致系统陷入一种混乱的状态。而且,如果状态改变的成功率较低,频繁地保存和恢复备忘录可能并不是一个高效的解决方案,不如采用其他更合适的设计模式,如 “假如” 协议模式(在执行操作前先假设操作成功,进行一系列模拟操作,若实际操作失败再进行回滚等处理)。例如,在一个金融交易系统中,每一笔交易操作都可能改变账户的状态,但交易过程中可能会因为各种原因(如网络故障、账户余额不足等)导致交易失败,如果频繁使用备忘录模式来恢复交易前的账户状态,可能会影响系统的稳定性和交易效率。
使用场景
可回滚的操作场景
-
文本编辑软件:像我们日常使用的 Word、WPS 等文本编辑软件,其中的撤销(Ctrl + Z)和重做(Ctrl + Y)功能就是备忘录模式的典型应用。在我们输入文字、修改格式、删除内容等操作过程中,文本编辑软件会在每次关键操作前创建一个备忘录,记录当前文档的状态。当我们按下撤销键时,软件就从之前保存的备忘录中取出对应的状态,将文档恢复到上一个操作前的状态;按下重做键时,则从后续保存的备忘录中获取状态,将文档恢复到撤销前的状态。
游戏存档场景
-
游戏存档是备忘录模式最为人熟知的应用场景之一。在各种类型的游戏中,玩家在游戏过程中会不断改变游戏角色的状态(如等级提升、装备获取、任务进度推进等)。为了让玩家能够在后续继续游戏时从之前保存的进度开始,游戏系统会在玩家进行存档操作时,创建一个备忘录记录游戏角色的当前状态,包括角色的各项属性(生命值、魔法值、攻击力等)、背包中的物品、所处的游戏地图位置、完成的任务列表等信息。当玩家读取存档时,游戏系统从对应的备忘录中获取这些信息,将游戏角色恢复到存档时的状态,让玩家能够继续之前的游戏体验。例如,在一款大型角色扮演游戏中,玩家经过长时间的探索和战斗,到达了一个关键的游戏场景,此时玩家选择存档。之后,当玩家再次打开游戏读取存档时,游戏角色会以存档时的状态出现在之前的游戏场景中,玩家可以继续后续的游戏冒险。
-
综上所述,备忘录模式在软件开发的众多领域中都有着广泛而重要的应用,它为我们提供了一种强大的状态管理和恢复机制,虽然存在一些缺点,但在合适的场景下合理运用,能够显著提升系统的质量和用户体验。在实际的软件设计和开发过程中,我们需要根据具体的业务需求和系统特点,权衡利弊,决定是否采用备忘录模式来解决状态管理相关的问题。
-
系统配置管理:在大型企业级系统中,系统配置的正确性对于系统的稳定运行至关重要。为了防止因为错误的配置修改导致系统故障,系统配置管理模块可以采用备忘录模式。在每次对系统配置进行修改前,创建一个备忘录记录当前的配置状态。如果修改后的配置导致系统出现问题,就可以从备忘录中恢复到之前正确的配置状态。比如,在一个云计算平台中,对服务器的网络配置、资源分配策略等进行修改时,利用备忘录模式可以保证在出现问题时能够快速回滚到稳定的配置状态。
需要监控的副本场景
-
数据库事务管理:数据库事务是指作为单个逻辑工作单元执行的一系列操作,这些操作要么全部成功,要么全部失败。在数据库事务处理中,备忘录模式被广泛应用于事务的回滚操作。当开始一个事务时,数据库系统会创建一个备忘录,记录事务开始前数据库的状态(包括数据的版本、锁的状态等)。如果在事务执行过程中出现错误,系统就可以根据备忘录中的信息将数据库恢复到事务开始前的状态,保证数据的一致性和完整性。例如,在一个银行转账事务中,当从一个账户扣除金额并向另一个账户添加金额的过程中,如果出现网络故障等问题,就可以利用备忘录模式回滚事务,确保两个账户的金额不会出现错误的变动。
-
命令行操作:在操作系统的命令行界面中,有时我们可能会连续执行多个命令,当发现某个命令执行错误时,希望能够回滚到之前的系统状态。一些先进的命令行工具就采用了备忘录模式,记录每次命令执行前系统的关键状态(如文件系统状态、环境变量等),当用户执行撤销命令时,能够恢复到之前正确的状态。
-
图形设计软件:在 Adobe Photoshop、Sketch 等图形设计软件中,用户对图形的绘制、变形、颜色调整等操作都可以通过备忘录模式实现可回滚。比如,当我们对一个图层进行了复杂的滤镜处理后,发现效果不理想,就可以利用备忘录模式撤销滤镜操作,恢复图层原来的状态。这种可回滚操作极大地提高了图形设计的灵活性和创作效率,让设计师能够更加大胆地尝试各种设计思路。