0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Pythonで実装するMementoパターン:状態をスナップショットとして保存・復元する

Posted at

概要

Memento(メメント)パターンは、
オブジェクトの内部状態を保存し、後からその状態に戻す(復元する)ことを可能にする設計パターンである。

主にUndo / Redo の実装、状態の巻き戻し、トランザクション処理などに応用される。
**「状態の履歴を取っておきたいが、内部構造を晒したくない」**という要件に対して非常に有効。


1. なぜMementoが必要か?

❌ 状態復元のためにオブジェクトの内部構造に触れてしまう

state = editor.__dict__.copy()  # 非公開な内部情報に依存

カプセル化が破られ、保守性・安全性が失われる


✅ 状態をMementoとして別オブジェクトに保存し、後から復元可能にする

snapshot = editor.save()
editor.restore(snapshot)

外部から状態を安全に操作でき、履歴管理が明確になる


2. 基本構造

✅ Memento(スナップショット)

class Memento:
    def __init__(self, content):
        self._state = content

    def get_state(self):
        return self._state

✅ Originator(状態を持つオブジェクト)

class TextEditor:
    def __init__(self):
        self._content = ""

    def write(self, text):
        self._content += text

    def save(self) -> Memento:
        return Memento(self._content)

    def restore(self, memento: Memento):
        self._content = memento.get_state()

    def show(self):
        print(f"[Editor] 現在の内容: {self._content}")

✅ Caretaker(履歴管理)

class History:
    def __init__(self):
        self._history = []

    def push(self, memento: Memento):
        self._history.append(memento)

    def pop(self) -> Memento:
        return self._history.pop()

✅ 使用例

editor = TextEditor()
history = History()

editor.write("Hello, ")
history.push(editor.save())

editor.write("World!")
history.push(editor.save())

editor.show()  # Hello, World!

editor.restore(history.pop())
editor.show()  # Hello,

editor.restore(history.pop())
editor.show()  # (空)

出力:

[Editor] 現在の内容: Hello, World!  
[Editor] 現在の内容: Hello,  
[Editor] 現在の内容: 

3. Python的応用:デコレータによる自動スナップショット保存

def with_snapshot(method):
    def wrapper(self, *args, **kwargs):
        self._history.append(self.save())
        return method(self, *args, **kwargs)
    return wrapper

class AutoEditor(TextEditor):
    def __init__(self):
        super().__init__()
        self._history = []

    @with_snapshot
    def write(self, text):
        super().write(text)

    def undo(self):
        if self._history:
            self.restore(self._history.pop())

操作のたびに自動的に履歴を残す実装も可能


4. 実務ユースケース

✅ テキストエディタのUndo/Redo機能

→ 書き換えのたびに状態を保存し、任意の時点に戻す


✅ フォーム入力の一時保存・リセット

ユーザー操作を巻き戻す


✅ ゲームのセーブ/ロード機構

→ スナップショットで状態を完全に記録・復元


✅ データベースや処理のトランザクション管理

→ 状態変更前後でロールバック可能な安全性を確保


5. よくある誤用と対策

❌ Mementoがオブジェクトの内部構造をそのまま持ってしまう

→ ✅ 内部状態のみを保持し、必要な最低限の情報に留める


❌ Mementoの数が増えすぎてメモリを圧迫

→ ✅ 履歴数に制限を設ける/スナップショットの差分化も検討


❌ OriginatorとCaretakerの責任が曖昧

→ ✅ Originatorは状態保存・復元、Caretakerは履歴管理に専念する


結語

Mementoパターンとは、“状態を一瞬で保存し、未来でそれを復元できる設計”である。

  • カプセル化を保ちつつ、状態の巻き戻しや履歴管理を構造的に実現
  • Undo/Redoや状態遷移管理において、データと操作の分離を可能にする
  • Pythonではクラスベースで明確に分離し、柔軟で直感的な状態管理が構築可能

Pythonicとは、“記録せずに戻るのではなく、戻れるように記録しておくこと”。
Mementoパターンはその記憶の知性を、設計の可逆性として定義する技法である。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?