备忘录模式

67 阅读3分钟

1. 简介

备忘录模式(Memento Pattern)是一种行为型设计模式,它将对象的状态快照的创建工作委派给实际状态的拥有者原发器(Originator)对象。另一方面,备忘录模式建议将对象状态的副本存储在一个名为备忘录(Memento)的特殊对象中。

2. 模式结构

classDiagram

    class Client {
        +main()
    }
    
    class Memento {
        -state: Any
        +getState(): Any
    }
    
    class Originator {
        -state: Any
        +operate()
    }
    
    class Caretaker {
        -originator: Originator
        -undoStack: List[Memento]
        -redoStack: List[Memento]
        +save()
        +undo()
        +redo()
    }
    
    Caretaker o-- Memento
    Originator ..> Memento
    Client ..> Originator
    Client ..> Caretaker
  1. 原发器 (Originator) 类可以生成自身状态的快照, 也可以在需要时通过快照恢复自身状态。
  2. 备忘录 (Memento) 是原发器状态快照的值对象 (value object)。 通常做法是将备忘录设为不可变的, 并通过构造函数一次性传递数据。
  3. 负责人 (Caretaker) 仅知道 “何时” 和 “为何” 捕捉原发器的状态, 以及何时恢复状态。

3. 使用场景以及优缺点

应用场景:

  • 需要提供撤销操作的功能。
  • 需要保存和恢复特定状态,但不希望暴露对象的内部实现。

优点:

  • 封装性:备忘录模式通过将状态的保存和恢复封装在不同的对象中,提高了系统的封装性。
  • 安全性:Originator的内部状态可以被保存而无需暴露给其他对象。
  • 可扩展性:可以容易地添加更多的状态或撤销级别。

缺点:

  • 资源消耗:如果保存的状态信息过多,可能会消耗大量的内存。
  • 复杂性:增加了系统的复杂性,因为需要额外的类和对象来实现备忘录模式。

4. 练习:【设计模式专题之备忘录模式】17-redo计数器应用

""" 
【设计模式专题之备忘录模式】17-redo计数器应用
时间限制:1.000S  空间限制:256MB
题目描述
小明正在设计一个简单的计数器应用,支持增加(Increment)和减少(Decrement)操作,以及撤销(Undo)和重做(Redo)操作,请你使用备忘录模式帮他实现。
输入描述
输入包含若干行,每行包含一个字符串,表示计数器应用的操作,操作包括 "Increment"、"Decrement"、"Undo" 和 "Redo"。
输出描述
对于每个 "Increment" 和 "Decrement" 操作,输出当前计数器的值,计数器数值从0开始 对于每个 "Undo" 操作,输出撤销后的计数器值。 对于每个 "Redo" 操作,输出重做后的计数器值。
输入示例
Increment
Increment
Decrement
Undo
Redo
Increment
输出示例
1
2
1
2
1
2
"""
from typing import List 
 

class Memento:
    def __init__(self, state: int):
        self._state = state
        
    @property
    def state(self) -> int:
        return self._state
        
        
class Counter:
    """ 充当备忘录模式的Originator """
    def __init__(self):
        self._value = 0
        
    @property 
    def value(self) -> int:
        return self._value
        
    @value.setter
    def value(self, v: int):
        self._value = v 
        
    def increment(self) -> Memento:
        mem = Memento(self._value)
        self._value += 1
        return mem
        
    def decrement(self) -> Memento:
        mem = Memento(self._value)
        self._value -= 1 
        return mem
        
        
class MemorableCounter:
    """ 充当备忘录模式的Caretaker """
    def __init__(self, counter: Counter):
        self._counter = counter
        self.undoStack: List[Memento] = []
        self.redoStack: List[Memento] = []
        
    def increment(self):
        self.redoStack.clear()  # 像VSCode这类编辑器的Redo功能貌似都是这样做的
        mem = self._counter.increment()
        self.undoStack.append(mem)
        print(self._counter.value)
        
    def decrement(self):
        self.redoStack.clear()
        mem = self._counter.decrement()
        self.undoStack.append(mem)
        print(self._counter.value)
        
    def undo(self):
        if not self.undoStack:
            return
        current = Memento(self._counter.value)
        self.redoStack.append(current)
        last = self.undoStack.pop()
        self._counter.value = last.state
        print(self._counter.value)
        
    def redo(self):
        if not self.redoStack:
            return
        current = Memento(self._counter.value)
        self.undoStack.append(current)
        last = self.redoStack.pop()
        self._counter.value = last.state
        print(self._counter.value)
        
        
def client():
    counter = Counter()
    caretaker = MemorableCounter(counter)
    while True:
        try:
            operation = input()
        except EOFError:
            break
        else:
            if operation == "Increment":
                caretaker.increment()
            if operation == "Decrement":
                caretaker.decrement()
            if operation == "Undo":
                caretaker.undo()
            if operation == "Redo":
                caretaker.redo()
        
        
if __name__ == "__main__":
    client()

5. 参考文章

  1. refactoringguru.cn/design-patt…
  2. github.com/youngyangya…