u16suzuの blog

日々学んだことのメモブログです。

Rubyでデザインパターン ~Stateパターン~

RubyでStateパターンを実装してみるテスト。

Stateのコンクリートクラスにメソッドの実装を生やすことで、Stateを新しく追加するときに追加しやすくしている。

クラスマクロもちょろっと使ってみたり。

でもクラスマクロはコードが短くなるのはいいけど、初見の人には読みづらいと思うんだよなぁ。

class Class
  def subclasses
    subclasses = []
    ObjectSpace.each_object(singleton_class) do |k|
      subclasses << k if k.superclass == self
    end
    subclasses
  end
end
class State
  @_time
  def self.time(time=nil) # 引数がnilの時はgetter, nilでない時はsetterになる
    @_time ||= time
  end
  def time # インスタンスメソッドも一応定義しておく
    self.class.instance_variable_get("@_time")
  end
  def hello
    raise
  end
end
class MorningState < State
  time 0..9
  
  def hello
    puts "Good morning!"
  end
end
class DayState < State
  time 10..15
  
  def hello
    puts "Hello!"
  end
end
class EveningState < State
  time 16..18

  def hello
    puts "Good evening."
  end
end
class NightState < State
  time 19..24
  
  def hello
    puts "Good night."
  end
end
class StateManager
  attr_accessor :state, :time
  def initialize(state=MorningState.new)
    @state = state
  end
  def say_hello
    @state.hello
  end
  def time=(time)
    @time = time
    State::subclasses.each do |k|
      if k.time.include?(@time)
        @state = k.new
        break
      end
    end
  end
end


mgr = StateManager.new
mgr.say_hello
(0..24).to_a.each do |i|
  mgr.time = i
  mgr.say_hello
end