7.1. Memento

7.1.1. Rationale

  • EN: Memento

  • PL: Pamiątka

  • Type: object

7.1.2. Use Cases

  • Undo operation

  • Remembering state of objects

7.1.3. Design

../../_images/designpatterns-memento-gof.png

7.1.4. Implementation

../../_images/designpatterns-memento-usecase.png
from dataclasses import dataclass, field
from typing import Final


@dataclass
class EditorState:
    __content: Final[str]

    def get_content(self):
        return self.__content


@dataclass
class History:
    __states: list[EditorState] = field(default_factory=list)

    def push(self, state: EditorState) -> None:
        self.__states.append(state)

    def pop(self) -> EditorState:
        return self.__states.pop()


class Editor:
    __content: str

    def set_content(self, content: str) -> None:
        self.__content = content

    def get_content(self) -> str:
        return self.__content

    def create_state(self):
        return EditorState(self.__content)

    def restore_state(self, state: EditorState):
        self.__content = state.get_content()


if __name__ == '__main__':
    editor = Editor()
    history = History()

    editor.set_content('a')
    history.push(editor.create_state())
    print(editor.get_content())
    # a

    editor.set_content('b')
    history.push(editor.create_state())
    print(editor.get_content())
    # b

    editor.set_content('c')
    print(editor.get_content())
    # c

    editor.restore_state(history.pop())
    print(editor.get_content())
    # b

7.1.5. Assignments

Code 7.21. Solution
"""
* Assignment: DesignPatterns Behavioral Memento
* Complexity: medium
* Lines of code: 10 lines
* Time: 13 min

English:
    1. Implement Memento pattern
    2. Run doctests - all must succeed

Polish:
    1. Zaimplementuj wzorzec Memento
    2. Uruchom doctesty - wszystkie muszą się powieść

Tests:
    >>> account = Account()
    >>> history = History()

    >>> account.deposit(100.00)
    >>> history.push(account.save())
    >>> account.get_balance()
    100.0

    >>> account.deposit(50.00)
    >>> history.push(account.save())
    >>> account.get_balance()
    150.0

    >>> account.deposit(25.00)
    >>> account.get_balance()
    175.0

    >>> account.undo(history.pop())
    >>> account.get_balance()
    150.0

"""
from dataclasses import dataclass, field
from typing import Final


@dataclass
class Transaction:
    def get_amount(self):
        raise NotImplementedError


@dataclass
class History:
    def push(self, transaction: Transaction) -> None:
        raise NotImplementedError

    def pop(self) -> Transaction:
        raise NotImplementedError


@dataclass
class Account:
    def deposit(self, amount: float) -> None:
        raise NotImplementedError

    def get_balance(self) -> float:
        raise NotImplementedError

    def save(self):
        raise NotImplementedError

    def undo(self, transaction: Transaction):
        raise NotImplementedError