Tbpgr Blog

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

RubyでProxyパターン/Matzは松江から来るのに時間がかかるのでProxyRubyistを置く!

概要

GoFデザインパターンのProxyパターンについて。
ある処理について、代理人を置くことで処理を分散して負荷を軽減させる。

登場人物

Subject = 主体
RealSubject = 主体の実体
Proxy = 代理人
Client = 利用者

UML


実装サンプル

サンプル概要

Rubyに関する質問をしたい人がいます。
この質問者は簡単な質問。普通の質問。難しい質問をします。
回答者となるRubyistには駆け出しRubyist、普通のRubyist、Matzがいます。
回答者の窓口となるのは駆け出しRubyistで、簡単な質問は自分で回答します。

駆け出しRubyistは普通の質問の答えが分からないので
普通のRubyistから答えを聞いて回答します。

駆け出しRubyistや普通のRubyistは難しいの質問の答えが分からないので
松江まで行ってMatzに答えを聞いて回答します。

Rubyistはレベルが高いほど多忙であるため、駆け出しRubyistが代理人となることで
普通のRubyistやMatszの負荷を軽減することが出来ます。

登場人物

Subject = Rubyist: Rubyの質問に回答するRubyist。主体役
RealSubject = Matz: Rubyの質問になんでも事が出来るRubyの創造神。主体の実体役
RealSubject = NormalRubyist: Rubyの質問に回答する普通のRubyist。主体の実体役
Proxy = BeginnerRubyist: Rubyの質問に回答する駆け出しRubyist。代理人役
Client = RubyQuestioner: Rubyの質問者。利用者役

UML


サンプルコード

RubyQuestioner

# encoding: Shift_JIS
require_relative './rubyist'

=begin rdoc
= RubyQuestionerクラス
=end
class RubyQuestioner
  attr_accessor :rubyist
  
  def initialize(rubyist)
    @rubyist = rubyist
  end
  def ask_easy_question
    puts "#{self.class}:どうやって文字を出力するの?"
    @rubyist.answer_easy_question
  end
  def ask_normal_question
    puts "#{self.class}:どうやってクラスを作成するの?"
    @rubyist.answer_normal_question
  end
  def ask_difficult_question
    puts "#{self.class}:どうやって名前空間の重複を解決するの?"
    @rubyist.answer_difficult_question
  end
end

Rubyist

# encoding: Shift_JIS

=begin rdoc
= Rubyistクラス
=end
class Rubyist
  attr_accessor :rubyist_map
  LEVEL_BEGGINER="begginer"
  LEVEL_NORMAL="normal"
  LEVEL_MASTER="mater"
  NOT_OVERRRIDE = 'not override error'
  
  def initialize(rubyist_map=nil)
    raise NOT_OVERRRIDE
  end
  def answer_easy_question()
    raise NOT_OVERRRIDE
  end
  def answer_normal_question()
    raise NOT_OVERRRIDE
  end
  def answer_difficult_question()
    raise NOT_OVERRRIDE
  end
end

BeginnerRubyist

# encoding: Shift_JIS

=begin rdoc
= BeginnerRubyistクラス
=end
class BeginnerRubyist
  def initialize(rubyist_map=nil)
    @rubyist_map=rubyist_map
  end
  def answer_easy_question()
    puts "#{self.class}:答えはputsだよ"
  end
  def answer_normal_question()
    if @rubyist_map[Rubyist::LEVEL_NORMAL]
      @rubyist_map[Rubyist::LEVEL_NORMAL].answer_normal_question
    elsif @rubyist_map[Rubyist::LEVEL_MASTER]
      @rubyist_map[Rubyist::LEVEL_MASTER].answer_normal_question
    else
      puts "#{self.class}:分かりません"
    end
  end
  def answer_difficult_question()
    if @rubyist_map[Rubyist::LEVEL_MASTER]
      @rubyist_map[Rubyist::LEVEL_MASTER].answer_difficult_question
    else
      puts "#{self.class}:分かりません"
    end
  end
end

NormalRubyist

# encoding: Shift_JIS

=begin rdoc
= NormalRubyistクラス
=end
class NormalRubyist
  def initialize(rubyist_map=nil)
    @rubyist_map=rubyist_map
  end
  def answer_easy_question()
    puts "#{self.class}:答えはputsだよ"
  end
  def answer_normal_question()
    puts "#{self.class}:答えはclassだよ"
  end
  def answer_difficult_question()
    if @rubyist_map[Rubyist::LEVEL_MASTER]
      @rubyist_map[Rubyist::LEVEL_MASTER].answer_difficult_question
    else
      puts "#{self.class}:分かりません"
    end
  end
end

Matz

# encoding: Shift_JIS

=begin rdoc
= Matzクラス
=end
class Matz
  def initialize(rubyist_map=nil)
    @rubyist_map=nil
  end
  def answer_easy_question()
    puts "#{self.class}:答えはputsだよ"
  end
  def answer_normal_question()
    puts "#{self.class}:答えはclassだよ"
  end
  def answer_difficult_question()
    puts "#{self.class}:答えはModuleだよ"
  end
end

main

# encoding: Shift_JIS
require_relative './ruby_questioner'
require_relative './rubyist'
require_relative './beginner_rubyist'
require_relative './normal_rubyist'
require_relative './matz'

rubyist_map = Hash.new
matz = Matz.new
normal_rubyist = NormalRubyist.new
rubyist_map[Rubyist::LEVEL_NORMAL] = normal_rubyist
rubyist_map[Rubyist::LEVEL_MASTER] = matz
beginner_rubyist = BeginnerRubyist.new(rubyist_map)

puts "−−−−−−−−−−−−−Beginnerが代理人を勤める時−−−−−−−−−−−−−"
ruby_questioner = RubyQuestioner.new(beginner_rubyist)
ruby_questioner.ask_easy_question
ruby_questioner.ask_normal_question
ruby_questioner.ask_difficult_question

# 以下はMatz不在時
puts "−−−−−−−−−−−−−Matz不在時−−−−−−−−−−−−−"
rubyist_map.delete(Rubyist::LEVEL_MASTER)
ruby_questioner.ask_easy_question
ruby_questioner.ask_normal_question
ruby_questioner.ask_difficult_question

# 以下はNormalRubyist不在時
puts "−−−−−−−−−−−−−NormalRubyist不在時−−−−−−−−−−−−−"
rubyist_map.delete(Rubyist::LEVEL_NORMAL)
rubyist_map[Rubyist::LEVEL_MASTER] = matz
ruby_questioner.ask_easy_question
ruby_questioner.ask_normal_question
ruby_questioner.ask_difficult_question

# 以下はMatzしかいない時
puts "−−−−−−−−−−−−−Matzしかいない時−−−−−−−−−−−−−"
matz = Matz.new
ruby_questioner = RubyQuestioner.new(matz)
ruby_questioner.ask_easy_question
ruby_questioner.ask_normal_question
ruby_questioner.ask_difficult_question
出力結果
−−−−−−−−−−−−−Beginnerが代理人を勤める時−−−−−−−−−−−−−
RubyQuestioner:どうやって文字を出力するの?
→BeginnerRubyist:答えはputsだよ
RubyQuestioner:どうやってクラスを作成するの?
→NormalRubyist:答えはclassだよ
RubyQuestioner:どうやって名前空間の重複を解決するの?
→Matz:答えはModuleだよ
−−−−−−−−−−−−−Matz不在時−−−−−−−−−−−−−
RubyQuestioner:どうやって文字を出力するの?
→BeginnerRubyist:答えはputsだよ
RubyQuestioner:どうやってクラスを作成するの?
→NormalRubyist:答えはclassだよ
RubyQuestioner:どうやって名前空間の重複を解決するの?
→BeginnerRubyist:分かりません
−−−−−−−−−−−−−NormalRubyist不在時−−−−−−−−−−−−−
RubyQuestioner:どうやって文字を出力するの?
→BeginnerRubyist:答えはputsだよ
RubyQuestioner:どうやってクラスを作成するの?
→Matz:答えはclassだよ
RubyQuestioner:どうやって名前空間の重複を解決するの?
→Matz:答えはModuleだよ
−−−−−−−−−−−−−Matzしかいない時−−−−−−−−−−−−−
RubyQuestioner:どうやって文字を出力するの?
→Matz:答えはputsだよ
RubyQuestioner:どうやってクラスを作成するの?
→Matz:答えはclassだよ
RubyQuestioner:どうやって名前空間の重複を解決するの?
→Matz:答えはModuleだよ

参照

RubyでProxyパターン/Matzは松江から来るのに時間がかかるのでProxyRubyistを置く!(Forwardable版)
http://d.hatena.ne.jp/tbpg/20121202/1354463011