定义
- 在不破坏内部结构的前提下捕获一个对象的内部状态,这样便可在以后将该对象恢复到原先保存的状态。
- 备忘模式的最大功能就是备份,可以保存对象的一个状态作为备份,这样便可让对象在将来的某一时刻恢复到之前保存的状态。
备忘模式的模型抽象
类图
- 精简版
- 升级版
- 区别之处
- Memento 不只能备份一个属性,而且能备份一组(多个)属性。
- Caretaker 能备份多个状态,Originator 可从中选择任意一个状态进行恢复。
代码框架
from copy import deepcopy
class Memento:
"""备忘录"""
def setAttributes(self, dict):
"""深度拷贝字典dict中的所有属性"""
self.__dict__ = deepcopy(dict)
def getAttributes(self):
"""获取属性字典"""
return self.__dict__
class Caretaker:
"""备忘录管理类"""
def __init__(self):
self._mementos = {}
def addMemento(self, name, memento):
self._mementos[name] = memento
def getMemento(self, name):
return self._mementos[name]
class Originator:
"""备份发起人"""
def createMemento(self):
memento = Memento()
memento.setAttributes(self.__dict__)
return memento
def restoreFromMemento(self, memento):
self.__dict__.update(memento.getAttributes())
模型说明
- 设计要点
- 备忘模式中主要有三个角色,在设计备忘模式时要找到并区分这些角色。
- 发起人(Originator):需要进行备份的对象。
- 备忘录(Memento):备份的状态,即一个备份的存档。
- 备忘录管理者(Caretaker):备份存档的管理者,由它负责与发起人的交互。
- 优缺点
- 优点:
- 提供了一种可以恢复状态的机制,使得用户能够比较方便地回到某个历史状态。
- 实现了信息的封装,用户不需要关心状态的保存细节。
- 缺点:
- 如果类的成员变量过多,势必会占用比较多的资源,而且每一次保存都会消耗一定的内存。此时可以限制保存的次数。
实战应用
from copy import deepcopy
import logging
class Memento:
"""备忘录"""
def setAttributes(self, dict):
"""深度拷贝字典dict中的所有属性"""
self.__dict__ = deepcopy(dict)
def getAttributes(self):
"""获取属性字典"""
return self.__dict__
class Caretaker:
"""备忘录管理类"""
def __init__(self):
self._mementos = {}
def addMemento(self, name, memento):
self._mementos[name] = memento
def getMemento(self, name):
return self._mementos[name]
class Originator:
"""备份发起人"""
def createMemento(self):
memento = Memento()
memento.setAttributes(self.__dict__)
return memento
def restoreFromMemento(self, memento):
self.__dict__.update(memento.getAttributes())
class TerminalCmd(Originator):
"""终端命令"""
def __init__(self, text):
self.__cmdName = ""
self.__cmdArgs = []
self.parseCmd(text)
def parseCmd(self, text):
"""从字符串中解析命令"""
subStrs = self.getArgumentsFromString(text, " ")
# 获取第一个字段作为命令名称
if len(subStrs) > 0:
self.__cmdName = subStrs[0]
# 获取第一个字段之后的所有字符作为命令的参数
if len(subStrs) > 1:
self.__cmdArgs = subStrs[1:]
def getArgumentsFromString(self, str, splitFlag):
"""通过splitFlag进行分割,获得参数数组"""
if splitFlag == "":
logging.warning("splitFlag 为空!")
return ""
data = str.split(splitFlag)
result = []
for item in data:
item.strip()
if item != "":
result.append(item)
return result
def showCmd(self):
print(self.__cmdName, self.__cmdArgs)
class TerminalCaretaker(Caretaker):
"""终端命令的备忘录管理类"""
def showHistoryCmds(self):
"""显示历史命令"""
for key, obj in self._mementos.items():
name = ""
value = []
if obj._TerminalCmd__cmdName:
name = obj._TerminalCmd__cmdName
if obj._TerminalCmd__cmdArgs:
value = obj._TerminalCmd__cmdArgs
print("第%s条命令: %s %s" % (key, name, value))
def testTerminal():
cmdIdx = 0
caretaker = TerminalCaretaker()
curCmd = TerminalCmd("")
while True:
strCmd = input("请输入指令:")
strCmd = strCmd.lower()
if strCmd.startswith("q"):
exit(0)
elif strCmd.startswith("h"):
caretaker.showHistoryCmds()
# 通过"!"符号表示获取历史的某个指令
elif strCmd.startswith("!"):
idx = int(strCmd[1:])
curCmd.restoreFromMemento(caretaker.getMemento(idx))
curCmd.showCmd()
else:
curCmd = TerminalCmd(strCmd)
curCmd.showCmd()
caretaker.addMemento(cmdIdx, curCmd.createMemento())
cmdIdx += 1
testTerminal()
应用场景
- 需要保存/恢复对象的状态或数据时,如游戏的存档、虚拟机的快照。
- 需要实现撤销、恢复功能的场景,如 Word 中的 Ctrl+Z、Ctrl+Y 功能,DOS 命令行或 Linux 终端的命令记忆功能。
- 提供一个可回滚的操作,如数据库的事务管理。