記念品パターン

メメントパターンは、オブジェクトのプライベートな内部状態を公開するソフトウェア設計パターンです。このパターンの活用例としては、オブジェクトを以前の状態に復元する(ロールバックによる元に戻す)、バージョン管理、カスタムシリアル化などが挙げられます。

メメントパターンは、originatorcaretakermemento の3 つのオブジェクトで実装されます。 originator は内部状態を持つオブジェクトです。 caretaker は originator に対して何らかの処理を行いますが、その変更を元に戻すことができるようにしたいと考えています。 caretaker はまず originator に memento オブジェクトを要求します。次に、実行しようとしていた操作 (または一連の操作) を実行します。操作前の状態にロールバックするには、 memento オブジェクトを originator に返します。 memento オブジェクト自体は opaque オブジェクト (caretaker が変更できない、または変更すべきでないオブジェクト) です。このパターンを使用する場合は、 originator が他のオブジェクトやリソースを変更する可能性があるかどうかに注意する必要があります。memento パターンは単一のオブジェクトに対して操作を行います。

メメント パターンの典型的な例としては、疑似乱数ジェネレータ(PRNG の各コンシューマーは、同じシード (メメント) を使用して PRNG (発信元) を初期化し、同一の疑似乱数シーケンスを生成できる管理者として機能します) や有限状態マシンの状態などがあります。

構造

UMLクラス図とシーケンス図

MementoデザインパターンのUMLクラス図とシーケンス図のサンプル。[1]

上記のUMLクラス図において、クラスは、オリジネータの内部状態を保存()および復元( )するためのクラスCaretakerを指します。このクラスは 、(1)オリジネータの現在の内部状態を保存するオブジェクトを作成して返すこと(2)渡されたオブジェクトから状態を復元することで実装します。OriginatorcreateMemento()restore(memento)
Originator
createMemento()Memento
restore(memento)Memento

UMLシーケンス図は実行時の相互作用を示しています。
(1) 発信元の内部状態の保存:オブジェクトはオブジェクトをCaretaker呼び出し、オブジェクトはオブジェクトを作成し、現在の内部状態()を保存し、オブジェクトをオブジェクトに返します(2) 発信元の内部状態の復元:オブジェクトはオブジェクト呼び出し、復元する状態を格納するオブジェクトを指定します。オブジェクトは、オブジェクトから状態()を取得して、自身の状態を設定します。createMemento()OriginatorMementosetState()MementoCaretaker
Caretakerrestore(memento)OriginatorMementoOriginatorgetState()Memento

Javaの例

次の Java プログラムは、memento パターンの「元に戻す」使用方法を示しています。

パッケージorg.wikipedia.examples ; java.util.ArrayListをインポートします。java.util.Listインポートします  class  Originator { private String state ; // クラスには、memento に保存された状態の一部ではない追加データも含めることができますpublic void set ( String state ) { this.state = state ; System.out.printf ( " Originator : state% s % n に設定しています" 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.printf ( " Originator : Mementoから復元後の状態: %s%n" 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では不変オブジェクトである文字列を状態として使用しています。実際のシナリオでは、状態はほとんどの場合可変オブジェクトとなるため、その場合は状態のコピーを作成する必要があります。

示した実装には欠点があります。内部クラスを宣言しているのです。このメメント戦略を複数のオリジネータに適用できれば、より良いでしょう。

Memento を達成するには、主に次の 3 つの方法があります。

  1. シリアル化。
  2. 同じパッケージ内で宣言されたクラス。
  3. オブジェクトにはプロキシ経由でアクセスすることもでき、これによりオブジェクトに対する保存/復元操作を実行できます。

C#の例

メメントパターンを使用すると、カプセル化を損なうことなくオブジェクトの内部状態をキャプチャできるため、必要に応じて後で変更を元に戻す/元に戻すことができます。ここでは、メメントオブジェクトが実際にオブジェクトに加えられた変更を元に戻すために使用されていることがわかります。

名前空間Wikipedia.Examples ; クラスMemento {プライベート読み取り専用文字列_savedState ;      プライベートMemento (文字列stateToSave ) {_ savedState = stateToSave ; }        public class Originator { private string _state ; // クラスには、メモに保存された状態の一部ではない追加データも含まれる場合があります。         public void Set ( string state ) { Console . WriteLine ( f "Originator: state を {state} に設定しています" ); _state = state ; }          public Memento SaveToMemento () { Console . WriteLine ( "Originator: Memento に保存しています。" ); return new Memento ( _state ); }         public void RestoreFromMemento ( Memento memento ) { _state = memento . savedState ; Console . WriteLine ( f "Originator: Memento から復元後の状態: {_state}" ); } } }          クラスCaretaker { static void Main ( string [] args ) { List < Memento > savedStates = new ();           Memento.Originator originator = new (); 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 : str = ""      def set ( self , state : str ) -> None : print ( f "発信者: 状態を{ state }に設定しています" ) self . state = state          def save_to_memento ( self ) - > " Memento " : return self.Memento ( self.state )      def restore_from_memento ( self , memento : "Memento" ) -> None : self . state = memento . get_saved_state () print ( f "Originator: Memento から復元後の状態: { self . state } " )          クラスMemento : state : str    def __init__ ( self , state : str ) -> None : self . state = state         def get_saved_state ( self ) - > str : self.state返す     saved_states : リスト[ Memento ]  =  [] originator :  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 パターンは、オブジェクトの状態を保存および復元するために使用されます。//メメントはオブジェクトの状態のスナップショットです。let Memento = { // 名前空間: Memento savedState : null , // オブジェクトの保存された状態。        save : function ( state ) { // オブジェクトの状態を保存します。this . savedState = state ; },         restore : function () { // オブジェクトの状態を復元します。return this .savedState ; } } ;       // Originator は、memento を作成するオブジェクトです。// memento 内の状態を保存するためのメソッドを定義します。let Originator = { //名前空間: Originator state : null , // 保存される状態        // 初期状態が null の新しいオリジネータを作成します。createMemento : function () { return { state : this.state //状態memento にコピーされます。}; } , setMemento : function ( memento ) { //オリジネータの状態を memento から設定します。this.state = memento.state ; } } ;                       // Caretaker はオブジェクトの mementos を格納し、それらを取得する操作を提供します。let Caretaker = { // 名前空間: Caretaker mementos : [ ], //オブジェクトの mementos。addMemento : function ( memento ) { // コレクションに memento を追加します。this . mementos . push ( memento ) ; }, getMemento : function ( index ) { // コレクションから memento を取得します。return this . mementos [ index ]; } };                       let action_step = "Foo" ; // 実行されるアクション/保存されるオブジェクトの状態。let 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 ) ; //バー   

参考文献

  1. ^ 「Mementoデザインパターン - 構造とコラボレーション」w3sDesign.com . 2017年8月12日閲覧
  • AdaにおけるMementoパターンの説明
  • C# および .NET コード サンプルを含む Memento UML クラス図
  • ソース作成チュートリアル
  • Javaを使用したMementoデザインパターン
Retrieved from "https://en.wikipedia.org/w/index.php?title=Memento_pattern&oldid=1320456494"