メメントパターンは、オブジェクトのプライベートな内部状態を公開するソフトウェア設計パターンです。これを使用する例としては、オブジェクトを以前の状態に復元する (ロールバックによる元に戻す) こと、バージョン管理、カスタム シリアル化などがあります。
メメント パターンは、originator、caretaker、mementoの 3 つのオブジェクトで実装されます。 originator は、内部状態を持つオブジェクトです。 caretaker は originator に対して何らかの操作を行いますが、変更を元に戻せるようにしたいと考えています。 caretaker は最初に originator に memento オブジェクトを要求します。次に、実行しようとしていた操作 (または操作のシーケンス) を実行します。操作前の状態にロールバックするには、 memento オブジェクトを originator に返します。 memento オブジェクト自体は不透明なオブジェクトです ( caretaker が変更できない、または変更すべきではないオブジェクト)。 このパターンを使用する場合、 originator が他のオブジェクトやリソースを変更する可能性がある場合は注意が必要です。 memento パターンは単一のオブジェクトに対して動作します。
メメント パターンの典型的な例としては、疑似乱数ジェネレータ(PRNG の各コンシューマーは、同じシード (メメント) を使用して PRNG (発信元) を初期化し、同一の疑似乱数シーケンスを生成できる管理者として機能します) や有限状態マシンの状態などがあります。
構造
UML クラス図とシーケンス図

上記の UML クラス図では、クラスは、発信者の内部状態を保存 ( ) および復元 ( ) するためのクラスCaretakerを指します。
クラスは
、発信者の現在の内部状態を格納するオブジェクトを作成して返すことによって
(1)を実装し、
渡されたオブジェクトから状態を復元することによって
(2)を実装します。
OriginatorcreateMemento()restore(memento)OriginatorcreateMemento()Mementorestore(memento)Memento
UMLシーケンス図は、実行時の相互作用を示しています。
(1) 発信元の内部状態の保存:オブジェクトは オブジェクトを呼び出し、オブジェクトはオブジェクトを作成し、その現在の内部状態 ( ) を保存して、を に返します。
(2) 発信元の内部状態の復元: はオブジェクトを呼び出し、復元する状態を格納するオブジェクトを指定します。は から状態 ( ) を取得して、独自の状態を設定します。
CaretakercreateMemento()OriginatorMementosetState()MementoCaretakerCaretakerrestore(memento)OriginatorMementoOriginatorgetState()Memento
Javaの例
次の Java プログラムは、memento パターンの「元に戻す」使用法を示しています。
import java.util.List ; import java.util.ArrayList ; class Originator { private String state ; // クラスには、memento に保存された状態の一部ではない追加データも含めることができます。public void set ( String state ) { this . state = state ; System . out . println ( "Originator: 状態を " + stateに設定しています。" ); } public Memento saveToMemento () { System . out . println ( "Originator: Memento に保存しています。" ); return new Memento ( this . state ); } public void restoreFromMemento ( Memento memento ) { this . state = memento . getSavedState (); System . out . println ( "Originator: Memento から復元した後の状態: " + state ); } public static class Memento { private final String state ;
public Memento ( String stateToSave ) { state = stateToSave ; } // 外部クラスからのみアクセス可能private String getSavedState () { return state ; } } } class Caretaker { public static void main ( String [] args ) { List < Originator . Memento > savedStates = new ArrayList < Originator . Memento > (); Originator originator = new Originator (); originator . set ( "State1" ); originator . set ( "State2" ); savedStates . add ( originator . saveToMemento ()); originator . set ( "State3" ); // 複数のメメントを要求し、どれにロールバックするかを選択できます。savedStates . add ( originator . saveToMemento ()); originator . set ( "State4" ); originator . restoreFromMemento ( savedStates . get ( 1 )); } }
出力は次のようになります。
発信者: 状態を State1 に設定する 発信者: 状態を State2 に設定する 作成者: Memento に保存します。 発信者: 状態を State3 に設定する 作成者: Memento に保存します。 発信者: 状態を State4 に設定する 作成者: Memento から復元後の状態: State3
この例では、Java では不変オブジェクトである String を状態として使用します。実際のシナリオでは、状態はほとんどの場合可変オブジェクトになるため、その場合は状態のコピーを作成する必要があります。
示されている実装には欠点があることを言わなければなりません。それは、内部クラスを宣言していることです。この memento 戦略が複数のオリジネータに適用できれば、さらに良いでしょう。
Memento を達成するには、主に次の 3 つの方法があります。
- シリアル化。
- 同じパッケージ内で宣言されたクラス。
- オブジェクトにはプロキシ経由でアクセスすることもでき、これによりオブジェクトに対する保存/復元操作を実行できます。
C#の例
メメント パターンを使用すると、カプセル化に違反することなくオブジェクトの内部状態をキャプチャできるため、後で必要に応じて変更を元に戻す/元に戻すことができます。ここでは、メメント オブジェクトが実際にオブジェクトに加えられた変更を 元に戻すために使用されていることがわかります。
クラスMemento { private readonly string savedState ;
プライベートMemento (文字列stateToSave ) { savedState = stateToSave ; }
public class Originator { private string state ; // クラスには、メメントに保存された状態の一部ではない追加データも含まれる場合があります。
public void Set ( string state ) { Console.WriteLine ( " Originator : state を " + stateに設定しています); this.state = state ; }
public Memento SaveToMemento () { Console . WriteLine ( "Originator: Memento に保存しています。" ); return new Memento ( state ); }
public void RestoreFromMemento ( Memento memento ) { state = memento . savedState ; Console . WriteLine ( "Originator: Memento から復元した後の状態: " + state ); } } }
クラスCaretaker { static void Main ( string [] args ) { var savedStates = new List < Memento > ();
var originator = new Memento.Originator ( ) ; originator.Set ( "State1" ) ; originator.Set ( " State2" ); savedStates.Add ( originator.SaveToMemento ( ) ); originator.Set ( "State3" ); // 複数のメメントを要求し、どれにロールバックするかを選択できます。savedStates.Add ( originator.SaveToMemento ( ) ) ; originator.Set ( " State4" );
originator.RestoreFromMemento ( savedStates [ 1 ] ) ; } }
Pythonの例
「
メメントパターンの例
」
クラス Originator :
_state = ""
def set ( self , state : str ) -> None :
print ( f "発信者: 状態を{ state }に設定しています" )
self . _state = state
def save_to_memento ( self ) -> " Memento " :
self.Memento ( self._state )を返し ます
def restore_from_memento ( self , m : "Memento" ) -> None :
self . _state = m . get_saved_state ()
print ( f "Originator: Memento から復元した後の状態: { self . _state } " )
クラス Memento :
def __init__ ( self 、 state ):
self . _state = state
def get_saved_state ( self )
: self._stateを返す
saved_states = [ ]
originator = Originator (
) originator.set ( " State1 " ) originator.set ( " State2 " ) saved_states.append ( originator.save_to_memento ( ) )
originator.set ( " State3" ) saved_states.append ( originator.save_to_memento ())で保存します。
発信元. set ( "State4" )
発信者.保存された状態から復元( [ 1 ])
Javascriptの例
// Memento パターンは、オブジェクトの状態を保存および復元するために使用されます。//メメントは、オブジェクトの状態のスナップショットです。var
Memento = { // 名前空間: Memento savedState : null , // オブジェクトの保存された状態。
save : function ( state ) { // オブジェクトの状態を保存します。this . savedState = state ; },
restore : function () { // オブジェクトの状態を復元します。return this .savedState ; } } ;
// Originator は、memento を作成するオブジェクトです。
// memento 内の状態を保存するためのメソッドを定義します。
var Originator = { // 名前空間: Originator state : null , // 保存される状態
// 初期状態が null の新しいオリジネータを作成します
。createMemento : function () { return { state : this.state // 状態が memento にコピーされます。}; } , setMemento : function ( memento ) { // オリジネータの状態をmementoから設定します。this.state = memento.state ; } } ;
// Caretaker はオブジェクトの mementos を格納し、それらを取得
するための操作を提供します。var
Caretaker = { // 名前空間: Caretaker mementos : [], // オブジェクトの mementos。addMemento : function ( memento ) { // コレクションに memento を追加します。this . mementos . push ( memento ) ; }, getMemento : function ( index ) { // コレクションから memento を取得します。return this . mementos [ index ] ; } };
var action_step = "Foo" ; // 実行されるアクション/保存されるオブジェクトの状態。var action_step_2 = "Bar" ; // 実行されるアクション/保存されるオブジェクトの状態。
//初期状態を設定しますOriginator.state = action_step ; Caretaker.addMemento ( Originator.createMemento ( ) ) ; //状態を履歴に保存しますconsole.log ( "初期状態: " + Originator.state ) ; // Foo
//状態を変更するOriginator.state = action_step_2 ; Caretaker.addMemento ( Originator.createMemento ( ) ); //状態を履歴に保存するconsole.log ( "変更後の状態: " + Originator.state ) ; //バー
//最初の状態を復元 - 元に戻すOriginator.setMemento (Caretaker.getMemento(0 ) ) ; console.log ( "元に戻す後の状態: " + Originator.state ) ; // Foo
// 2番目の状態を復元-やり直しOriginator.setMemento ( Caretaker.getMemento ( 1 )); console.log ( "やり直し後の状態: " + Originator.state ) ; // バー
参考文献
- ^ 「Memento デザイン パターン - 構造とコラボレーション」w3sDesign.com 。 2017 年 8 月 12 日閲覧。
外部リンク
- Ada の Memento パターンの説明
- C# および .NET コード サンプルを含む Memento UML クラス図
- ソース作成チュートリアル
- Java を使用した Memento デザイン パターン
