概要
GoFのデザインパターンのMementoパターンについて。
ある時点でのスナップショットを残すことによって
undo,redo,snapshot,history
などの機能を実現します。
Memento(=記念品)にはwide interfaceとnarrow interfaceを用意します。
wide interfaceはprotectedで定義し、MementoとOriginatorで共有する情報を定義します。
narrow interfaceはpublicで定義し、Caretakerからも参照できるようにします。
これにより、カプセル化が破壊されることを防止します。
実装サンプル
サンプル概要
擬似クリップボード機能を実装します。
ユーザーが入力を行うとUserInputにデータを保持します。
任意のタイミングでDummyClipboardに対して以下の操作が出来ます。
UserInputのデータを追加する
DummyClipboardからデータを取り出してUserInputに設定する(undo)
undoした状態ならredoを行うことでUserInputの状態をredo以前の状態に戻す
DummyClipboardから操作履歴の一覧を取得する
登場人物
Memento = DummyClipboard : 疑似クリップボード。記念品役。
Originator = UserInput : ユーザーの入力情報。創始者役。
Caretaker = User : 入力ユーザー。世話人役。
サンプルコード
user
# encoding: Shift_JIS require_relative './dummy_clipboard' require_relative './user_input' =begin rdoc = Userクラス =end class User attr_accessor :user_input,:undo_list,:redo_list def initialize() @user_input = UserInput.new @undo_list = Array.new @redo_list = Array.new end def input(text) @user_input.input(text) clipboard = @user_input.get_clipboard @undo_list.push clipboard end def undo() undo_element = @undo_list.pop unless undo_element.nil? unless @undo_list.last.nil? @user_input.input(@undo_list.last.get_text) @redo_list.push undo_element end end end def redo() redo_element = @redo_list.pop unless redo_element.nil? @user_input.input(redo_element.get_text) @undo_list.push redo_element.get_text end end def history() @undo_list.each{|undo_element|print "#{undo_element.get_text}:"} end end
user_input
# encoding: Shift_JIS require_relative './dummy_clipboard' =begin rdoc = UserInputクラス =end class UserInput attr_accessor :text def initialize() @text = "" end def input(text) @text = text end def get_clipboard() DummyClipboard.new(@text) end def undo(dummy_clipboard_list) previous_input = dummy_clipboard_list.pop @text = previous_input return previous_input end def redo(dummy_clipboard_list,redo_list) previous_undo = redo_list.pop @text = previous_undo return previous_undo end end
dummy_clipboard
# encoding: Shift_JIS =begin rdoc = DummyClipboardクラス =end class DummyClipboard attr_writer :text def initialize(text) @text = text end def get_text() return @text end end
main
# encoding: Shift_JIS require_relative './user' #require_relative './user_input' #require_relative './dummy_clipboad' user = User.new user.input "first" puts "inputで入力更新+クリップボード追加:#{user.user_input.text}" user.input "second" puts "inputで入力更新+クリップボード追加:#{user.user_input.text}" user.input "third" puts "inputで入力更新+クリップボード追加:#{user.user_input.text}" print "クリップボードのヒストリを表示:" user.history puts "" user.undo puts "undoで入力を戻す+クリップボードからポップ:#{user.user_input.text}" user.undo puts "undoで入力を戻す+クリップボードからポップ:#{user.user_input.text}" user.undo puts "undoでエラーにならないことを確認:#{user.user_input.text}" user.redo puts "redoで入力を再戻し+再戻し用クリップボードからポップ:#{user.user_input.text}" user.redo puts "redoで入力を再戻し+再戻し用クリップボードからポップ:#{user.user_input.text}" user.redo puts "redoでエラーにならないことを確認:#{user.user_input.text}"
出力結果
inputで入力更新+クリップボード追加:first inputで入力更新+クリップボード追加:second inputで入力更新+クリップボード追加:third クリップボードのヒストリを表示:first:second:third: undoで入力を戻す+クリップボードからポップ:second undoで入力を戻す+クリップボードからポップ:first undoでエラーにならないことを確認:first redoで入力を再戻し+再戻し用クリップボードからポップ:second redoで入力を再戻し+再戻し用クリップボードからポップ:third redoでエラーにならないことを確認:third