Memento (メメント) パターン

インスタンスのその瞬間瞬間をスナップショットとして記録しておき、
元の状態へ戻す事を容易に実現するデザインパターンです。
テキストエディタ等で、Ctrl + Z で元に戻したり、元に戻したものを Ctrl + Y でやり直したり
しますよね。元に戻すだけでなく、ある時点における状態を復元する事も簡単に実現できます。


サンプルでは、画面にユーザーが設定した色の保存・復元・やり直しを行います。

このカテゴリーの先頭へ このページの先頭へ

■クラス図

ここで用いるサンプルのクラス図です。
準備中...

このカテゴリーの先頭へ このページの先頭へ

■サンプルの説明

サンプルの見た目1
背景色ボタンをクリックすると、ColorDialog コントロールを用いてユーザーが任意の色を選択します。
Ctrl + S で、現在の色を保存し、Ctrl + Z で、直前に設定していた色を表示します。
また、Ctrl + Z で戻したあと、やはり戻る前の色に戻したい場合は、Ctrl + Y で元に戻ります。
サンプルのプロジェクトダウンロード

このカテゴリーの先頭へ このページの先頭へ

■コード

アプリケーションのエントリポイント StartUpForm です。この画面では、
ColorDialog コントロールを使用しています。これについての詳細はじゃんぬさんのページを参照下さい。

StartUpForm.vb
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です。

OriginatorLabel.vb
'背景色を保存できるラベル
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
このカテゴリーの先頭へ このページの先頭へ

保存・復元をするオブジェクトのスナップショットを表現するクラスです。

Memento.vb
'スナップショットを表すクラス
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 については修正の必要が発生しません。

このカテゴリーの先頭へ このページの先頭へ