Tbpgr Blog

Employee Experience Engineer tbpgr(てぃーびー) のブログ

RubyでCommandパターン/たたかう・ぼうぎょ・じゅもん・アイテム/ガンガンいこうぜ・いのちをだいじに・いろいろやろうぜ

概要

GoFデザインパターンのCommandパターンについて。
処理をクラスとして独立させることで、再利用性を高めたり
複数履歴履歴に対応したりさせることが出来る。

よくある用例

登場人物

Command = コマンド
ConcreteCommand = 具象コマンド
Receiver = 受信者
Invoker = 起動者
Client = 利用者

UML


実装サンプル

サンプル概要

RPGを想定します。
ユーザーがメニューからコマンドを選択すると画面にそのコマンドの実行結果が表示されます。
メニューには作戦だけ指定する方法と手動入力する方法があります。
手動入力は

  たたかう
  ぼうぎょ
  じゅもん
  アイテム

作戦は

  ガンガンいこうぜ:たたかう、じゅもんをランダムに選択
  いのちをだいじに:ぼうぎょ、じゅもんをランダムに選択
  いろいろやろうぜ:じゅもん、アイテムをランダムに選択

の三種類です。
また、初心者救済としてコマンドの実行前の状態に戻すことが可能です。

登場人物

Command = RpgCommand : このRPGのコマンド。コマンド役
ConcreteCommand = AtackCommand : たたかう。具象コマンド役
ConcreteCommand = GuardCommand : 防御。具象コマンド役
ConcreteCommand = CastSpellCommand : 呪文を唱える。具象コマンド役
ConcreteCommand = UseItemCommand : アイテムを使う。具象コマンド役
Invoker = BattleMenu : 戦闘メニュー。起動者役
Client = RpgGamer : RPGを遊ぶゲーマー。利用者役

UML


サンプルコード

RpgCommand

# encoding: Shift_JIS

=begin rdoc
= RpgCommandクラス
=end
class RpgCommand
  NOT_OVERRIDE = 'not override error'
  GANGAN = 0
  INOCHI = 1
  IROIRO = 2
  
  def act()
    raise NOT_OVERRIDE
  end
  
  def cancel()
    raise NOT_OVERRIDE
  end
end

AtackCommand

# encoding: Shift_JIS

=begin rdoc
= AttackCommandクラス
=end
class AttackCommand < RpgCommand
  def act()
    puts "攻撃!"
  end
  
  def cancel()
    puts "攻撃を中止"
  end
end

GuardCommand

# encoding: Shift_JIS

=begin rdoc
= GuardCommandクラス
=end
class GuardCommand < RpgCommand
  def act()
    puts "防御!"
  end
  
  def cancel()
    puts "防御を中止"
  end
end

CastSpellCommand

# encoding: Shift_JIS

=begin rdoc
= CastSpellCommandクラス
=end
class CastSpellCommand < RpgCommand
  def act()
    puts "魔法を唱えた!"
  end
  
  def cancel()
    puts "魔法を唱えるのを中止"
  end
end

UseItemCommand

# encoding: Shift_JIS

=begin rdoc
= UseItemCommandクラス
=end
class UseItemCommand < RpgCommand
  def act()
    puts "アイテムを使った!"
  end
  
  def cancel()
    puts "アイテムを使うのを中止"
  end
end

BattleMenu

# encoding: Shift_JIS
require_relative './rpg_command'
require_relative './attack_command'
require_relative './guard_command'
require_relative './cast_spell_command'
require_relative './use_item_command'

=begin rdoc
= BattleMenuクラス
=end
class BattleMenu
  attr_accessor :battle_menu,:previous_command

  def attack()
    attack_command = AttackCommand.new
    act attack_command
  end
  
  def guard()
    guard_command = GuardCommand.new
    act guard_command
  end
  
  def cast_spell()
    cast_spell_command = CastSpellCommand.new
    act cast_spell_command
  end
  
  def use_item()
    use_item_command = UseItemCommand.new
    act use_item_command
  end
  
  def cancel()
    @previous_command.cancel
  end
  
  def auto(type)
    command = RpgCommand.new
    case type
    when RpgCommand::GANGAN
      command = random_half_selector(AttackCommand,CastSpellCommand)
    when RpgCommand::INOCHI
      command = random_half_selector(GuardCommand,CastSpellCommand)
    when RpgCommand::IROIRO
      command = random_half_selector(CastSpellCommand,UseItemCommand)
    end
    act command
  end

  private
  def act(command)
    command.act
    @previous_command = command
  end
  
  def random_half_selector(selectionA,selectionB)
    random = Random.new
    random_number = random.rand(2)
    if random_number == 0
      return selectionA.new
    else
      return selectionB.new
    end
  end
end

RpgGamer

# encoding: Shift_JIS
require_relative './battle_menu'

=begin rdoc
= RpgGamerクラス
=end
class RpgGamer
  attr_accessor :battle_menu

  def initialize()
    @is_auto = false
    @battle_menu = BattleMenu.new
  end

  def attack()
    battle_menu.attack
  end
  
  def guard()
    battle_menu.guard
  end
  
  def cast_spell()
    battle_menu.cast_spell
  end
  
  def use_item()
    battle_menu.use_item
  end
  
  def cancel()
    battle_menu.cancel
  end
  
  def auto(type)
    battle_menu.auto type
  end
end

main

# encoding: Shift_JIS
require_relative './rpg_gamer'

rpg_gamer = RpgGamer.new
print "マニュアル:攻撃:"
rpg_gamer.attack
print "マニュアル:防御:"
rpg_gamer.guard
print "マニュアル:呪文:"
rpg_gamer.cast_spell
print "マニュアル:アイテム:"
rpg_gamer.use_item
print "マニュアル:キャンセル:"
rpg_gamer.cancel

5.times{
  print "ガンガンいこうぜ:"
  rpg_gamer.auto RpgCommand::GANGAN
}
  print "ガンガンいこうぜ:キャンセル:"
rpg_gamer.cancel
5.times{
  print "いのちをだいじに:"
  rpg_gamer.auto RpgCommand::INOCHI
}
print "いのちをだいじに:キャンセル:"
rpg_gamer.cancel
5.times{
  print "いろいろやろうぜ:"
  rpg_gamer.auto RpgCommand::IROIRO
}
print "いろいろやろうぜ:キャンセル:"
rpg_gamer.cancel
出力結果
マニュアル:攻撃:攻撃!
マニュアル:防御:防御!
マニュアル:呪文:魔法を唱えた!
マニュアル:アイテム:アイテムを使った!
マニュアル:キャンセル:アイテムを使うのを中止
ガンガンいこうぜ:魔法を唱えた!
ガンガンいこうぜ:攻撃!
ガンガンいこうぜ:魔法を唱えた!
ガンガンいこうぜ:攻撃!
ガンガンいこうぜ:攻撃!
ガンガンいこうぜ:キャンセル:攻撃を中止
いのちをだいじに:防御!
いのちをだいじに:防御!
いのちをだいじに:魔法を唱えた!
いのちをだいじに:魔法を唱えた!
いのちをだいじに:防御!
いのちをだいじに:キャンセル:防御を中止
いろいろやろうぜ:魔法を唱えた!
いろいろやろうぜ:魔法を唱えた!
いろいろやろうぜ:魔法を唱えた!
いろいろやろうぜ:アイテムを使った!
いろいろやろうぜ:魔法を唱えた!
いろいろやろうぜ:キャンセル:魔法を唱えるのを中止