Command pattern - Design Pattern in Ruby
「何かを実行するもの」をオブジェクトとして扱う方法。
これによる恩恵として、実行の記録の保管が容易になり、undo, redoなどの実装もしやすくなる。
よく使用されるケースとしては、GUIのパーツで、パーツのデザインや配置などの処理と、何かしらのアクションに応じた処理を切り離して管理することができる。
compositeパターンの考え方を用いてCompositeCommandを実装すると、複数のコマンドを単一のコマンドと同じように扱うことができる。
簡易実装
ボードにピースを配置するという簡単なプログラムで、ピースの追加、移動等の操作をCommandとして実装した。
class Board attr_accessor :pieces def initialize (size) @pieces = Array.new size @commands = [] @command_index = 0 end def execute_command(command) puts command.describe command.execute display_state @commands[@command_index] = command @command_index += 1 end def undo puts 'Undo:' command = @commands[@command_index - 1] puts "\t" + command.describe command.unexecute display_state @command_index -= 1 end def display_state state = '|' @pieces.each do |ele| if ele state << ele.name else state << ' ' end state << '|' end puts state end end class Piece attr_accessor :name def initialize(name) @name = name end end class CompositeCommand def initialize @commands = [] end def add_command (command) @commands << command end def execute @commands.each {|c| c.execute} end def unexecute @commands.reverse.each {|c| c.unexecute} end def describe @commands.map{|c| c.describe}.join "\n" end end class AddPieceCommand def initialize (piece, index, board) @piece = piece @index = index @board = board @previousPiece end def execute @previousPiece = @board.pieces[@index] @board.pieces[@index] = @piece end def unexecute @board.pieces[@index] = @previousPiece end def describe "Add Piece: #{@piece.name} to #{@index}" end end board = Board.new 8 initial_command = CompositeCommand.new initial_command.add_command AddPieceCommand.new Piece.new('A'), 2, board initial_command.add_command AddPieceCommand.new Piece.new('B'), 4, board board.execute_command initial_command # Add Piece: A to 2 # Add Piece: B to 4 # | | |A| |B| | | | board.undo # Undo: # Add Piece: A to 2 # Add Piece: B to 4 # | | | | | | | | |