Memento (メメント) パターン
インスタンスのその瞬間瞬間をスナップショットとして記録しておき、
元の状態へ戻す事を容易に実現するデザインパターンです。
テキストエディタ等で、Ctrl + Z で元に戻したり、元に戻したものを Ctrl + Y でやり直したり
しますよね。元に戻すだけでなく、ある時点における状態を復元する事も簡単に実現できます。
サンプルでは、画面にユーザーが設定した色の保存・復元・やり直しを行います。
■クラス図
ここで用いるサンプルのクラス図です。
準備中...
■サンプルの説明

背景色ボタンをクリックすると、ColorDialog コントロールを用いてユーザーが任意の色を選択します。
Ctrl + S で、現在の色を保存し、Ctrl + Z で、直前に設定していた色を表示します。
また、Ctrl + Z で戻したあと、やはり戻る前の色に戻したい場合は、Ctrl + Y で元に戻ります。
サンプルのプロジェクトダウンロード
■コード
アプリケーションのエントリポイント StartUpForm です。この画面では、
ColorDialog コントロールを使用しています。これについての詳細はじゃんぬさんのページを参照下さい。
Public Class StartUpForm Inherits System.Windows.Forms.Form #Region " Windows フォーム デザイナで生成されたコード " Public Sub New() MyBase.New() ' この呼び出しは Windows フォーム デザイナで必要です。 InitializeComponent() ' InitializeComponent() 呼び出しの後に初期化を追加します。 AddHandler Application.ThreadException, AddressOf CommonExceptionAndHandler.ExceptionHandler.MyHandler End Sub ' Form は、コンポーネント一覧に後処理を実行するために dispose をオーバーライドします。 Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean) If disposing Then If Not (components Is Nothing) Then components.Dispose() End If End If MyBase.Dispose(disposing) End Sub ' Windows フォーム デザイナで必要です。 Private components As System.ComponentModel.IContainer ' メモ : 以下のプロシージャは、Windows フォーム デザイナで必要です。 'Windows フォーム デザイナを使って変更してください。 ' コード エディタを使って変更しないでください。 Friend WithEvents Button1 As System.Windows.Forms.Button <System.Diagnostics.DebuggerStepThrough()> Private Sub InitializeComponent() Me.Button1 = New System.Windows.Forms.Button Me.SuspendLayout() ' 'Button1 ' Me.Button1.Location = New System.Drawing.Point(104, 8) Me.Button1.Name = "Button1" Me.Button1.TabIndex = 0 Me.Button1.Text = "Button1" ' 'StartUpForm ' Me.AutoScaleBaseSize = New System.Drawing.Size(5, 12) Me.ClientSize = New System.Drawing.Size(292, 266) Me.Controls.Add(Me.Button1) Me.Name = "StartUpForm" Me.Text = "Form1" Me.ResumeLayout(False) End Sub #End Region '状態を保持しているArrayList Private m_mementoCollection As ArrayList '現在表示している状態のindex Private m_currentPos As Integer '状態(背景色)保持可能なLabel Private m_originatorLabel As OriginatorLabel '画面がロードされた時のイベント Private Sub StartUpForm_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Load Me.Text = "Memento" Me.KeyPreview = True Me.Button1.Text = "背景色" Me.m_originatorLabel = New OriginatorLabel Me.m_originatorLabel.Location = New Point(10, 40) Me.m_originatorLabel.Size = New Size(270, 200) Me.Controls.Add(Me.m_originatorLabel) Dim tip As ToolTip = New ToolTip tip.SetToolTip(Me.m_originatorLabel, "『Ctrl + S』で保存、『Ctrl + Z』で Undo、『Ctrl + Y』で Redo") Me.m_mementoCollection = New ArrayList Me.m_mementoCollection.Add(Me.m_originatorLabel.CreateMement()) Me.m_currentPos = 0 End Sub '背景色ボタン押下時のイベント Private Sub Button1_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles Button1.Click Dim cd As ColorDialog = New ColorDialog Try cd.Color = Me.m_originatorLabel.BackColor cd.FullOpen = True cd.AnyColor = True cd.SolidColorOnly = False cd.ShowHelp = True If cd.ShowDialog(Me) = DialogResult.OK Then Me.m_originatorLabel.BackColor = cd.Color Me.m_originatorLabel.Refresh() End If Finally If Not cd Is Nothing Then cd.Dispose() End Try End Sub 'フォーム上でキー押下時のイベント Private Sub StartUpForm_KeyDown(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyEventArgs) _ Handles MyBase.KeyDown If e.Control AndAlso e.KeyValue = Keys.S Then 'Ctrl + S が押下された時(保存) Me.m_mementoCollection.Add(Me.m_originatorLabel.CreateMement()) Me.m_currentPos += 1 MessageBox.Show("現在の色を保存しました", _ Me.Text, _ MessageBoxButtons.OK, _ MessageBoxIcon.Information) ElseIf e.Control AndAlso e.KeyValue = Keys.Z Then 'Ctrl + Z が押下された時(Undo) If Me.m_currentPos - 1 >= 0 Then Me.m_currentPos -= 1 Me.m_originatorLabel.RestoreMemento(DirectCast(Me.m_mementoCollection(Me.m_currentPos), Memento)) End If ElseIf e.Control AndAlso e.KeyValue = Keys.Y Then 'Ctrl + Y が押下された時(Redo) If Me.m_currentPos + 1 <= Me.m_mementoCollection.Count - 1 Then Me.m_currentPos += 1 Me.m_originatorLabel.RestoreMemento(DirectCast(Me.m_mementoCollection(Me.m_currentPos), Memento)) End If End If End Sub End Class
背景色を保存・復元できるようにした Labelです。
'背景色を保存できるラベル Public Class OriginatorLabel Inherits Label '現在の状態を保存 Public Function CreateMement() As Memento Dim mem As Memento = New Memento mem.AddMement(MyBase.BackColor) Return mem End Function '以前の状態を復元 Public Sub RestoreMemento(ByVal mem As Memento) Me.BackColor = mem.BackColor End Sub End Class
保存・復元をするオブジェクトのスナップショットを表現するクラスです。
'スナップショットを表すクラス Public Class Memento 'この状態の時の背景色 Private m_mements As Color '背景色 Public ReadOnly Property BackColor() As Color Get Return Me.m_mements End Get End Property 'この背景色の状態を保存する Public Sub AddMement(ByVal bkcolor As Color) Me.m_mements = bkcolor End Sub End Class
■ひとこと
サンプルでは状態を保存する対象を Label の背景色なんて、単純なものでやってみましたが、
実務で使うとしたら、TextBox のようなユーザーが入力するコントロールが沢山配置されている
ユーザーコントロールになんか適応すると便利かもしれません。
また、OriginatorLabel の RestoreMemento メソッドによって、背景色を実際に戻している処理は隠蔽されています。
これに、背景色以外の情報の保存等が加わったり、保存先がメモリ上の HashTable ではなく、DB になったとしても、
状態の復元の実際の処理はカプセル化されているので、
StartUpForm については修正の必要が発生しません。