私の意見では、UNDO/REDO は大きく 2 つの方法で実装できます。1. コマンド レベル (コマンド レベルの取り消し/やり直しと呼ばれる) 2. ドキュメント レベル (グローバルな取り消し/やり直しと呼ばれる)
コマンド レベル: 多くの回答が指摘しているように、これは Memento パターンを使用して効率的に達成されます。コマンドがアクションのジャーナル化もサポートしている場合、やり直しは簡単にサポートされます。
制限: コマンドの範囲外になると、元に戻す/やり直しが不可能になり、ドキュメント レベル (グローバル) の元に戻す/やり直しが発生します。
多くのメモリ空間を必要とするモデルに適しているため、あなたのケースはグローバルな元に戻す/やり直しに適合すると思います。また、これは選択的に元に戻す/やり直しにも適しています。2 つのプリミティブ型があります
- すべてのメモリの取り消し/やり直し
- オブジェクト レベル 元に戻す やり直し
「全メモリ Undo/Redo」では、メモリ全体を接続されたデータ (ツリー、リスト、グラフなど) として扱い、メモリは OS ではなくアプリケーションによって管理されます。そのため、C++ の new 演算子と delete 演算子は、a などの操作を効果的に実装するためのより具体的な構造を含むようにオーバーロードされます。いずれかのノードが変更された場合、b. データの保持とクリアなど、基本的に機能する方法は、メモリ全体をコピーし(メモリ割り当てが高度なアルゴリズムを使用してアプリケーションによって既に最適化および管理されていると仮定)、スタックに保存します。メモリのコピーが要求された場合、浅いコピーまたは深いコピーの必要性に基づいてツリー構造がコピーされます。ディープ コピーは、変更された変数に対してのみ作成されます。すべての変数はカスタム割り当てを使用して割り当てられるため、アプリケーションは、必要に応じていつ削除するかを最終的に決定します。一連の操作をプログラムで選択的に元に戻す/やり直す必要がある場合に、元に戻す/やり直しを分割する必要がある場合、事態は非常に興味深いものになります。この場合、これらの新しい変数、または削除された変数または変更された変数のみにフラグが与えられるため、元に戻す/やり直しはそれらのメモリのみを元に戻す/やり直すことができます。オブジェクト内で部分的な元に戻す/やり直す必要がある場合、事態はさらに興味深いものになります。そんな時は「ビジターパターン」という新しい発想が使われます。「オブジェクト レベルの取り消し/やり直し」と呼ばれます。または、削除された変数または変更された変数にはフラグが与えられるため、元に戻す/やり直しはそれらのメモリのみを元に戻す/やり直す オブジェクト内で部分的な元に戻す/やり直す必要がある場合、事態はさらに興味深いものになります。そんな時は「ビジターパターン」という新しい発想が使われます。「オブジェクト レベルの取り消し/やり直し」と呼ばれます。または、削除された変数または変更された変数にはフラグが与えられるため、元に戻す/やり直しはそれらのメモリのみを元に戻す/やり直す オブジェクト内で部分的な元に戻す/やり直す必要がある場合、事態はさらに興味深いものになります。そんな時は「ビジターパターン」という新しい発想が使われます。「オブジェクト レベルの取り消し/やり直し」と呼ばれます。
- オブジェクト レベルの元に戻す/やり直し: 元に戻す/やり直しの通知が呼び出されると、すべてのオブジェクトはストリーミング操作を実装します。ストリーマーは、プログラムされた古いデータ/新しいデータをオブジェクトから取得します。乱されないデータはそのまま残されます。すべてのオブジェクトは引数としてストリーマーを取得し、UNDo/Redo 呼び出し内で、オブジェクトのデータをストリーミング/アンストリーミングします。
1 と 2 の両方に、1. BeforeUndo() 2. AfterUndo() 3. BeforeRedo() 4. AfterRedo() などのメソッドを含めることができます。これらのメソッドは、特定のアクションを取得するためにすべてのオブジェクトがこれらのメソッドを実装するように、基本的な元に戻す/やり直しコマンド (コンテキスト コマンドではない) で公開する必要があります。
優れた戦略は、1 と 2 のハイブリッドを作成することです。これらのメソッド (1 と 2) 自体がコマンド パターンを使用している点が優れています。