Tbpgr Blog

Ruby プログラマ tbpgr(てぃーびー) のブログ

Ruby | Command Pattern

概要

Command Pattern

詳細

Command Patternは、命令を実行するためのパターンです。
命令をCommandとしてクラスにしておくことで、実行方法と命令を分離することができます。
これにより
・命令の履歴を保存する
・Undo機能を持つ
・命令をためてから任意のタイミングで実行する
などが簡単に実現できるようになります。

利用例

RailsマイグレーションはCommandパターンにより、Undo機能を持たせたよい例です。
upメソッドとdownメソッドでdo/undoを切り替えることができます。

サンプル1仕様

囲み文字つきメッセージを表示する機能があるとします。
囲み文字つきのメッセージをMessageとして定義し、
各メッセージの集合をMessagesとして定義します。
各メッセージはMessageViewerで表示します。

サンプル1

require 'attributes_initializable'

class SurroundMessage
  include AttributesInitializable
  attr_reader_init :message, :surround

  def show
    "#{@surround}#{@message}#{@surround}"
  end
end

class Messages
  include Enumerable
  include AttributesInitializable
  attr_reader_init :messages

  def each
    @messages.each { |message|yield message }
  end
end

class MessageViewer
  include AttributesInitializable
  attr_reader_init :messages
  
  def show
    @messages.each { |message|puts message.show }
  end
end

messages = Messages.new({messages: [SurroundMessage.new({message: 'hoge', surround: '@'}), SurroundMessage.new({message: 'hige', surround: '$'})]})
mv = MessageViewer.new(messages: messages)
mv.show

__END__
AttributesInitializableは tbpgr_utils gemの機能です。
AttributesInitializableのincludeとattr_accessor_initで
attr_accessorの宣言とコンストラクタでのメンバへの変数設定を一気にやっていると思ってください。

tbpgr_utils gemについては下記参照
https://github.com/tbpgr/tbpgr_utils

出力

@hoge@
$hige$

upメソッドとdownメソッドでdo/undoを切り替えることができます。

サンプル2仕様

サンプル1に加えて大文字・小文字メッセージを扱うUpperLowerMessageを追加します。
サンプル1に加えて逆順にメッセージを表示するためにReverseMessageViewerも作成します。

このように、命令と実行が分離しているためそれぞれ影響を与えずに追加や変更が可能になります。

サンプル2

require 'attributes_initializable'

class SurroundMessage
  include AttributesInitializable
  attr_reader_init :message, :surround

  def show
    "#{@surround}#{@message}#{@surround}"
  end
end

class UpperLowerMessage
  include AttributesInitializable
  attr_reader_init :message, :up_down

  def show
    message.send @up_down
  end
end

class Messages
  include Enumerable
  include AttributesInitializable
  attr_reader_init :messages

  def each
    @messages.each { |message|yield message }
  end
end

class MessageViewer
  include AttributesInitializable
  attr_reader_init :messages
  
  def show
    @messages.each { |message|puts message.show }
  end
end

class ReverseMessageViewer
  include AttributesInitializable
  attr_reader_init :messages

  def show_desc
    @messages.reverse_each { |message|puts message.show }
  end
end

messages = Messages.new({
    messages: [
      SurroundMessage.new({message: 'hoge', surround: '@'}),
      SurroundMessage.new({message: 'hige', surround: '$'}),
      UpperLowerMessage.new({message: 'hAgE', up_down: :upcase}),
      UpperLowerMessage.new({message: 'HaGe', up_down: :downcase}),
    ]
  })
mv = MessageViewer.new(messages: messages)
mv.show
rmv = ReverseMessageViewer.new(messages: messages)
rmv.show_desc

__END__
AttributesInitializableは tbpgr_utils gemの機能です。
AttributesInitializableのincludeとattr_accessor_initで
attr_accessorの宣言とコンストラクタでのメンバへの変数設定を一気にやっていると思ってください。

tbpgr_utils gemについては下記参照
https://github.com/tbpgr/tbpgr_utils

出力

@hoge@
$hige$
HAGE
hage
hage
HAGE
$hige$
@hoge@